No. Werkzeug's secure_filename stops a client from using a filename like ../../home/user/.bashrc to write outside your upload folder, but it says nothing about what is inside the file, how big it is, or how it will be served later. Treat it as one control in a checklist.
What secure_filename protects against
Flask's file upload pattern starts from the principle never trust user input, which applies to the uploaded file's name as well. The docs show the danger: imagine a client sends a filename made of repeated parent directory segments leading to a path such as home/username/.bashrc. If you join that with your UPLOAD_FOLDER, the user may be able to modify a file on the server that they should not.
secure_filename reduces a name to a safe form, stripping directory components so the file can only be saved inside the folder you chose:
from werkzeug.utils import secure_filename
filename = secure_filename(file.filename)
file.save(os.path.join(app.config["UPLOAD_FOLDER"], filename))
What it does not do
| Concern | secure_filename helps? |
|---|---|
| Path traversal through the name | Yes |
| File content is what the extension says | No |
| File size | No, use MAX_CONTENT_LENGTH |
| Overwriting another user's file with the same name | No |
| Serving HTML or script from uploads | No |
| Malware or oversized images | No |
The Flask docs add that you should limit allowed extensions, because you probably do not want users uploading everything, for example HTML files that would run script on your domain when served. Their example uses an allow list of txt, pdf, png, jpg, jpeg and gif. An extension check is still a hint, since an attacker controls the name. Check the actual content for expected types where it matters.
A fuller checklist
- Use secure_filename, or better, ignore the user's name and generate your own, for example a random identifier plus a safe extension.
- Store uploads outside the web root and outside the application code directory.
- Set MAX_CONTENT_LENGTH and per route limits so a large body is refused early.
- Allow only the extensions and content types you need, and verify content for images and documents.
- Never overwrite existing files. Generated names avoid collisions and attacks on other users' files.
- Serve uploads with a correct content type and the Content-Disposition attachment header, as Flask's security page advises for files from uploads. The same page warns not to send out HTML from uploaded files.
- Consider serving user content from a separate domain so that any script runs without your cookies.
- Scan files if your users share them with each other.
The serving side is where XSS comes in
Flask's security page notes that browsers may guess the content type of text files from the first bytes, so a crafted file could be treated as HTML. Combine an explicit content type with attachment disposition to remove the guesswork.
Using send_from_directory
When you return files to users, use the framework helper that limits the path to a directory rather than joining paths yourself, and never pass user input straight to a file open call.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how path handling and file operations are flagged.
Quick check
Should I trust the file extension?
Only as a first filter. The client controls the filename, so verify content for types that matter.
Is a random filename better than secure_filename?
Often yes. It avoids collisions and removes the user's text from your file system. Keep the original name as metadata if you need it.
Safeguard's SAST and dependency scanning can flag unsafe file handling in code and vulnerable Flask or Werkzeug versions. Try a free scan.
Sources
Last reviewed: 19 October 2026.