CSV MIME Type – Free, Online, Private
text/csv is the type for comma-separated values. RFC 4180's default charset is US-ASCII, so send charset=utf-8.
What .csv is
A CSV file is text records separated by newlines, with fields separated by commas. RFC 4180 allows a field to be quoted when it contains a comma, a quote, or a line break, and a quote inside a quoted field is doubled. There is no schema in the file. A header row is conventional and not required by the type. The registration's default charset, if you omit the parameter, is US-ASCII. Real exports are UTF-8, often with a BOM that spreadsheet programs used to need in order to notice the encoding. The type is text/csv.
IANA registers text/csv in RFC 4180 and RFC 7111. The media type is text/csv. Extensions that use it on this page: .csv. A client compares the type, not the filename, once the response has arrived. The filename still matters on disk and in Content-Disposition, which is why a wrong extension and a wrong header are two different bugs.
What sends text/csv
What sends text/csv: Send text/csv; charset=utf-8 for a download and for an API that returns a table. Add a filename in Content-Disposition because the URL may not end in .csv and a spreadsheet will otherwise invent a name. If the first row is headers, leave it as data. Do not switch to application/csv, which is not the registered type. RFC 7111 defines a fragment for selecting rows and columns. The fragment is part of the URL, not the Content-Type. Set it only if a client you control understands it. The file on disk is still .csv. Once the response exists, a client trusts text/csv, not the filename. For a very large extract, stream the rows. Do not build one string. The type stays text/csv on a chunked response. If you also offer JSON and XLSX, give each its own URL and its own type. A query parameter that changes the body format needs Vary or, better, a different path.
Content-Type for .csv
Send text/csv; charset=utf-8. The charset parameter names the encoding of the body. Leaving it off forces the client to guess, and for several text types the registered default is not UTF-8. Do not send a charset on a redirect. The header belongs on the response that contains the bytes.
Content-TypeContent-Type: text/csv; charset=utf-8
nginx, Apache, and Express
These snippets map .csv to text/csv. nginx reads the types block when it picks a type from the URL's extension. A location that sets default_type, or an application that sets the header itself, overrides that map. Apache AddType applies in the directory context where you put it. The Express static example writes the header for files ending in .csv so a stale mime database in the framework cannot disagree with the type on this page.
nginx# /etc/nginx/mime.types, inside the types { } block
text/csv csv;
charset utf-8;
charset_types text/csv;
ApacheAddType text/csv .csv
AddCharset UTF-8 .csv
Expressimport express from "express";
const app = express();
app.use(express.static("public", {
setHeaders(res, filePath) {
if (filePath.endsWith(".csv")) {
res.setHeader("Content-Type", "text/csv; charset=utf-8");
}
},
}));
Pitfalls for text/csv
Pitfalls that show up for text/csv, and not for a neighboring type with a similar extension: Opening UTF-8 CSV in a spreadsheet that assumed the system code page is how non-ASCII names turn into garbage. The charset parameter fixes browsers. It does not fix a desktop tool that ignores HTTP and looks for a BOM. A second trap is a field that itself contains a newline, which a line-oriented splitter truncates. Use a parser. A third is formula injection: a cell that starts with =, +, -, or @ can run as a formula when a person opens the download. Prefix or escape those cells if the data is untrusted. A fourth is serving CSV as text/html, which turns the file into a page if a browser sniffs it.
- A semicolon-separated file is not the RFC 4180 comma profile. Do not call it text/csv and also tell people it is Excel's locale default unless you document the separator.
- text/csv does not imply a comma in every locale. The type says commas. If you emit semicolons, say so next to the URL.
- A UTF-8 BOM is the three bytes EF BB BF. Some parsers keep it as part of the first header name.
- Gzip is worthwhile. Keep text/csv and set Content-Encoding.
Frequently asked questions
Should I send a charset?
Yes. text/csv; charset=utf-8. Without the parameter the registered default is US-ASCII.
Is application/csv registered?
No. The registered type is text/csv.
Does the type require a header row?
No. A header row is a convention. The media type is the same with or without one.
How do I stop spreadsheet formula injection?
Escape cells that start with =, +, -, or @ when the source is untrusted. The Content-Type will not do that for you.