How to Secure Images and Media on the Web
Protect uploaded files with layered validation, storage and access controls. Use CSP to limit where pages load images, video, audio and text tracks.
Secure images and media with separate controls for uploaded files and browser fetches. Validate and limit uploads, generate storage names, restrict access, and store files outside the webroot or on a separate server when feasible. Then use a Content Security Policy (CSP) to limit which origins the page may fetch images and media from. CSP controls browser requests; it does not validate or sanitize uploaded files.
This guide covers the security boundary from upload to display: what to validate, where to store files, how to set img-src and media-src, and how to roll out and troubleshoot a policy. The exact allowlist depends on the origins and features your site actually uses.
1. Separate upload security from display policy
There are two different questions:
- Upload and storage: Is this file allowed, within limits, associated with an authorized uploader, and stored so it cannot overwrite another file or be served unexpectedly?
- Display and fetching: Which origins may a browser page request images, video, audio, and text tracks from?
Answer the first with application-side validation, generated names, limits, authorization, and storage controls. Answer the second with CSP. A restrictive img-src or media-src does not make a malicious or malformed uploaded file safe. OWASP describes file-upload risks including parser vulnerabilities, active content, oversized files, overwrites, and public retrieval risks. Its guidance recommends layered defenses rather than relying on one validation check. OWASP File Upload Cheat Sheet.
2. Build a layered upload pipeline
Allow only the file types the feature needs
Define an allowlist for each upload feature. For example, an avatar feature should accept only its intended image types; a video feature may need a different set. Do not accept every type merely because the browser can display it. The client-supplied Content-Type is not proof of the file’s contents and can be spoofed.
Validate content and metadata on the server
Check the actual file type using server-side inspection appropriate to your application. Also validate the filename and extension against the allowlist, but do not treat an extension or MIME header alone as sufficient. If files are processed or transformed, include that processing pipeline in the threat model: parser vulnerabilities can affect the transformation service too. Antivirus or sandbox scanning can add a layer where available, but does not replace other checks.
Set limits and generate storage names
- Enforce a maximum upload size before expensive processing, and apply limits that match the feature.
- Generate the storage name in the application. Do not use the client filename as the storage path.
- Limit filename length and handle unexpected names safely.
- Prevent overwrites by ensuring generated names are unique within the storage system.
- Authorize the uploader before accepting or associating a file with an account or resource.
Keep storage and delivery intentional
When feasible, store uploads outside the webroot or on a separate server. Decide explicitly whether each file is private or publicly retrievable, who can retrieve it, and how it is served. Public file serving can introduce disclosure, denial-of-service, and harmful-content risks. If public access is needed, make that an explicit part of the access-control and serving design rather than an accidental consequence of where the file was saved.
Use this implementation checklist as a baseline, adapting it to the application’s file types and processing path:
- Authorize the uploader.
- Enforce request and file-size limits.
- Allowlist the file types required by this feature.
- Inspect file content server-side; do not trust the request’s
Content-Type. - Validate the filename and limit its length.
- Generate a storage name in the application.
- Store outside the webroot or separately where feasible.
- Apply the intended private or public retrieval rules.
- Scan with antivirus or a sandbox where available.
- Include any file transformation and public serving behavior in threat modeling.
3. Restrict image and media origins with CSP
CSP lets developers control resources a page may fetch. The W3C CSP specification defines img-src for image requests and media-src for video, audio, and associated text tracks. Start with the origins your page requires, then allow only those origins. Avoid broad wildcards unless the site genuinely needs them. W3C Content Security Policy Level 3.
For example, if images are served by the same origin and media.example.com, while audio, video, and tracks come from the same origin and media.example.com, a policy could include:
Content-Security-Policy: img-src 'self' https://media.example.com; media-src 'self' https://media.example.com
Replace https://media.example.com with origins your site actually uses. An origin includes the scheme, host, and port. Add each legitimate media host individually. If your site does not need third-party media, do not add it just in case.
These directives address browser fetch sources; they are not upload validators. A page policy also needs to account for other resources the application uses. Treat this example as the relevant image and media portion of a policy, not a complete policy for every page feature.
4. Deliver the policy and roll it out safely
Prefer delivering CSP in the HTTP response header across responses. A <meta> policy is a limited fallback and does not support all CSP features. MDN’s CSP guide and OWASP’s CSP guidance describe report-only mode as a way to observe violations before enforcing a stricter policy. MDN CSP guide; OWASP CSP Cheat Sheet.
- Inventory: List upload flows, media features, and the origins used by images, video, audio, and text tracks.
- Apply upload controls: Add authorization, allowlists, content validation, size limits, generated names, and deliberate storage and serving rules.
- Start with the needed sources: Write the image and media source restrictions from observed application requirements.
- Observe: Use a report-only policy to identify legitimate fetches that your proposed policy would block. Report-only does not enforce the policy.
- Fix and enforce: Update the allowlist for legitimate dependencies, then deploy an enforcing response header.
- Monitor: Revisit the policy when a feature or media origin changes.
Do not copy a policy from another site without checking your own page. A broad allowlist can weaken the boundary; an incomplete one can break legitimate content.
5. Serve pages and resources over HTTPS
Use HTTPS for pages and their subresources. MDN recommends HTTPS, CSP, secure handling of untrusted input, and threat modeling based on a site’s features. Check that the origins in the policy use the schemes your site actually serves and that your media references do not depend on insecure requests. MDN Web Security.
6. Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| An uploaded file is accepted even though its request says it is an image | The application trusted the client-supplied Content-Type. |
Inspect the actual file content server-side, allowlist needed types, and use multiple checks rather than relying on one header. |
| A legitimate image is blocked by CSP | Its origin is missing from img-src, or the policy uses a different scheme, host, or port. |
Identify the actual image origin, add only that required origin, and check the page again. |
| Video, audio, or captions fail while images work | The media source is not allowed by media-src. Associated text tracks are covered by this directive too. |
Check the origins used by the media and text tracks and allow the required origin in media-src. |
| Report-only violations appear but content still loads | That is expected: report-only mode reports policy violations without enforcing the restrictions. | Use the observed violations to find legitimate dependencies and fix the policy before switching to enforcement. |
| Media works in one page but fails in another | The pages may use different response policies or different media origins. | Inspect the response header for the affected page and compare it with that page’s actual media dependencies. |
| Files overwrite each other or expose client filenames | The client filename was used directly as the storage name. | Generate names in the application, limit filename length, and prevent collisions. |
| Large uploads consume excessive resources | There is no effective upload size limit, or processing starts before limits are enforced. | Set limits that match the feature and include processing costs in the upload pipeline design. |
| Private uploads can be fetched publicly | Storage or serving behavior made files reachable without the intended access checks. | Keep files outside the webroot or on separate storage where feasible, and define retrieval authorization explicitly. |
7. Performance, reliability, and cost considerations
- Validation and processing: Content inspection, scanning, and transformations add work to an upload path. Apply size limits and decide which processing stages are required for each feature. The appropriate pipeline depends on the application’s types and threat model.
- Storage and public retrieval: Separating storage from the webroot can make serving behavior more deliberate, but requires the application to define how authorized retrieval works. Public availability should be a conscious choice.
- CSP rollout: Report-only mode helps reveal breakage before enforcement. Allowlist only verified dependencies; overly broad sources reduce the value of the restriction, while missing legitimate sources can block content.
- HTTPS and origins: Ensure pages and subresources use HTTPS and keep policy origins aligned with the actual deployment configuration.
The reviewed guidance does not establish a universally best storage provider, performance benchmark, or cost figure. Choose infrastructure and limits based on the application’s media volume, access model, and operational requirements.
8. Inspect the rendered page and its media behavior
After changing upload handling or CSP, inspect representative pages that display uploaded and administrator-controlled media. Check a page with images, a page with video or audio, and any page that loads text tracks. Confirm the relevant origins and investigate blocked-resource messages before enforcing a stricter policy. A screenshot can make a visual regression easier to spot, but it does not validate file contents or prove that an upload pipeline is secure.
For a manual browser check, open the affected page in a browser, inspect its network and console output, and compare blocked requests with the page’s intended media dependencies. Keep the policy response header and upload validation as separate review items.
Or skip the browser setup
For a visual check of a page after you change its media policy, ScreenshotNeo can capture the rendered page with one GET request. It is a website screenshot API and MCP server; it helps inspect what the page displays, while upload validation and CSP remain your application’s responsibility. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.
FAQ
Does CSP make uploaded files safe?
No. CSP limits browser fetch sources. Validate and handle uploaded files in the application.
Should all uploaded media be public?
No. Decide whether each class of file is private or publicly retrievable and implement serving and authorization accordingly.
Can I use a meta tag instead of a response header?
A meta policy is a limited fallback. Prefer an HTTP response header, which supports broader policy delivery.
Should I allow every CDN in the CSP?
Allow only origins the site needs. First inventory the page’s legitimate media dependencies.


