1.8 KiB
title, order, status
| title | order | status |
|---|---|---|
| S06. Return a File Download Response | 25 | draft |
To force a browser to show a download dialog instead of rendering inline, send a Content-Disposition header. There's no special cpp-httplib API for this — it's just a header.
Basic usage
svr.Get("/download/report", [](const httplib::Request &req, httplib::Response &res) {
res.set_header("Content-Disposition", "attachment; filename=\"report.pdf\"");
res.set_file_content("reports/2026-04.pdf", "application/pdf");
});
Content-Disposition: attachment makes the browser pop up a "Save As" dialog. The filename= parameter becomes the default save name.
Non-ASCII file names
For file names with non-ASCII characters or spaces, use the RFC 5987 filename* form.
svr.Get("/download/report", [](const httplib::Request &req, httplib::Response &res) {
res.set_header(
"Content-Disposition",
"attachment; filename=\"report.pdf\"; "
"filename*=UTF-8''%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88.pdf");
res.set_file_content("reports/2026-04.pdf", "application/pdf");
});
The part after filename*=UTF-8'' is URL-encoded UTF-8. Keep the ASCII filename= too, as a fallback for older browsers.
Download dynamically generated data
You don't need a real file — you can serve a generated string as a download directly.
svr.Get("/export.csv", [](const httplib::Request &req, httplib::Response &res) {
std::string csv = build_csv();
res.set_header("Content-Disposition", "attachment; filename=\"export.csv\"");
res.set_content(csv, "text/csv");
});
This is the classic pattern for CSV exports.
Note: Some browsers will trigger a download based on Content-Type alone, even without
Content-Disposition. Conversely, settinginlinetries to render the content in the browser when possible.