Bug description
/api/v1/dashboard/export/ and sibling export endpoints for charts/datasets/) return:
Cache-Control: public, max-age=31536000
Expires: <+1 year>
because Flask's send_file() falls back to app.config['SEND_FILE_MAX_AGE_DEFAULT'] when the call site doesn't pass max_age. That default, in superset/config.py:
# Cache static resources.
SEND_FILE_MAX_AGE_DEFAULT = int(timedelta(days=365).total_seconds())
should be meant for hashed static assets (JS/CSS/images), but none of the export endpoints override it, so every dynamic zip export inherits a public 1-year cache lifetime.
Steps to reproduce
- Dashboard List → bulk-select one or more dashboards → Export.
- Immediately export the identical dashboard set again (same ids → identical
?q= query string).
- In Chrome: the 2nd request is served
(from disk cache) — 0 bytes transferred, ~0ms wait. If the underlying dashboards changed between step 1 and step 2, the downloaded zip is silently stale (identical bytes to the first export).
Expected results
Every export request is regenerated server-side and never served from browser cache, regardless of whether the query string repeats.
Actual results
The browser reuses the previous response for an identical query string, because the server explicitly marks the response public, max-age=31536000.
Suggested fix
Pass max_age=0 explicitly in the send_file() calls used by export endpoints (superset/dashboards/api.py::export, and the equivalent methods in the chart/dataset APIs), instead of inheriting the global static-asset default.
Screenshots/recordings
No response
Superset version
6.1.0
Python version
3.11
Node version
Not applicable
Browser
Chrome
Additional context
Additional context
Checklist
Bug description
/api/v1/dashboard/export/and sibling export endpoints for charts/datasets/) return:because Flask's
send_file()falls back toapp.config['SEND_FILE_MAX_AGE_DEFAULT']when the call site doesn't passmax_age. That default, insuperset/config.py:should be meant for hashed static assets (JS/CSS/images), but none of the export endpoints override it, so every dynamic zip export inherits a public 1-year cache lifetime.
Steps to reproduce
?q=query string).(from disk cache)— 0 bytes transferred, ~0ms wait. If the underlying dashboards changed between step 1 and step 2, the downloaded zip is silently stale (identical bytes to the first export).Expected results
Every export request is regenerated server-side and never served from browser cache, regardless of whether the query string repeats.
Actual results
The browser reuses the previous response for an identical query string, because the server explicitly marks the response
public, max-age=31536000.Suggested fix
Pass
max_age=0explicitly in thesend_file()calls used by export endpoints (superset/dashboards/api.py::export, and the equivalent methods in the chart/dataset APIs), instead of inheriting the global static-asset default.Screenshots/recordings
No response
Superset version
6.1.0
Python version
3.11
Node version
Not applicable
Browser
Chrome
Additional context
Additional context
fix(export): replace iframe with fetch to avoid CSP frame-src violations)[Config] Cache static resources) — introducedDEFAULT_HTTP_HEADERS/OVERRIDE_HTTP_HEADERSChecklist