Static Files
Table of Contents
Why Static Files Matter
Backend applications often need to serve files that do not change for every request. These are called static files. They are usually:
- Images, icons, logos
- CSS stylesheets
- JavaScript files
- Fonts
- Sometimes PDFs or other documents
Your backend must know where these files live on disk and how to send them to the client efficiently and securely.
Static files are any files that are sent “as is”, without being generated for each request.
Dynamic pages are usually HTML generated by templates or code. Static files are plain files on disk that the server reads and sends.
Typical Static File Structure
Most projects use a dedicated folder for static assets, for example:
my_project/
app/
routes.py
templates/
base.html
index.html
static/
css/
main.css
js/
app.js
images/
logo.png
banner.jpgCommon conventions:
| Type | Typical Folder | Examples |
|---|---|---|
| Stylesheets | static/css | main.css, reset.css |
| JavaScript | static/js | app.js, analytics.js |
| Images | static/images | logo.png, avatar.jpg |
| Fonts | static/fonts | Roboto.woff2, icons.ttf |
| Other assets | static/ | robots.txt, favicon.ico |
The exact layout depends on the framework, but the pattern is always:
- One or more source directories on disk
- URLs that point to files inside those directories
Static Files vs Templates
Templates are covered in their own chapter, but it is important to understand how static files relate to them.
- Templates are often HTML files that contain placeholders. The backend fills those placeholders before sending the result.
- Static files are usually referenced inside templates using fixed URLs.
Example HTML template that uses a static CSS file and a static image:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My Site</title>
<link rel="stylesheet" href="/static/css/main.css">
</head>
<body>
<img src="/static/images/logo.png" alt="Logo">
<h1>Welcome</h1>
<script src="/static/js/app.js"></script>
</body>
</html>Here the backend must be configured so that:
/static/css/main.cssmaps tostatic/css/main.csson disk/static/images/logo.pngmaps tostatic/images/logo.pngon disk/static/js/app.jsmaps tostatic/js/app.json disk
How Static File Serving Works
At a high level, serving a static file follows this flow:
- Client requests a URL
Example:GET /static/css/main.css HTTP/1.1 - Server maps the URL to a file path
Example mapping rule: - URL prefix
/static/ - Filesystem directory
/app/static/
So/static/css/main.cssbecomes/app/static/css/main.css - Server checks if file exists and is allowed
- If yes, it reads the file and sends it
- If no, it returns 404 Not Found
- Server sets headers such as:
Content-Type: text/cssContent-Length: 1234- Maybe caching headers like
Cache-Control - Browser receives and uses the file
For CSS, it applies the styles. For JavaScript, it executes the script.
You usually do not implement this logic manually. Your framework, web server, or reverse proxy will do it for you, but you need to configure:
- The directory or directories where static files are stored
- The URL prefix used to access them, such as
/static/or/assets/
Static Files in a Minimal Python Web App
Even before you use a full framework, you can create a simple example using a basic ASGI or WSGI app.
Here is a tiny example using the http.server module from the Python standard library. This is not production ready, but it shows the idea:
from http.server import SimpleHTTPRequestHandler, HTTPServer
PORT = 8000
# This handler serves files from the current directory by default.
handler_class = SimpleHTTPRequestHandler
with HTTPServer(("0.0.0.0", PORT), handler_class) as httpd:
print(f"Serving on http://localhost:{PORT}")
httpd.serve_forever()
If you run this in a directory that contains a static folder, you will be able to access files like:
http://localhost:8000/static/css/main.csshttp://localhost:8000/static/js/app.js
In real backend applications you will configure static files explicitly in the framework or in a web server like Nginx, but the concept is the same.
Static Files and URLs
The URL path for static files does not have to match the folder name. You might have:
- Folder:
static/ - URL prefix:
/assets/
So this mapping is possible:
| URL | File path on disk |
|---|---|
/assets/css/main.css | static/css/main.css |
/assets/js/app.js | static/js/app.js |
/assets/logo.png | static/images/logo.png |
You control this mapping through configuration.
Why change the URL prefix?
- To avoid conflicts with other routes, for example if you already have
/staticfor something else. - To add versioning, such as
/static/v1/vs/static/v2/. - For security or consistency with an existing system.
Static Files During Development vs Production
In development you might let your framework serve static files directly because it is simple.
Example pattern:
- Run the app with a development server
- Framework serves both:
- Dynamic routes like
/,/api/users - Static files like
/static/css/main.css
In production, a common pattern is:
- A reverse proxy or dedicated web server handles static files
- The backend application only handles dynamic endpoints
A typical production setup:
| Part | Responsibility |
|---|---|
| Nginx (or similar) | Serve /static/, /media/, handle HTTPS |
| Backend app | Serve /api/*, /login, other dynamic routes |
The idea is that web servers like Nginx are very good at:
- Reading files from disk
- Caching them
- Handling lots of concurrent connections
So they are more efficient for static files than application code.
Static Files and Caching
Static files usually do not change often, so they are a perfect target for caching.
Caching improves speed and reduces load by allowing the browser or intermediate caches to reuse the same file.
Common strategies:
- Set long cache lifetimes for static files
For example:
Cache-Control: public, max-age=31536000This tells the browser it can cache the file for a year.
- Use file versioning or hashes
When you change the file, change its name or URL, for example: - Old:
/static/css/main.css - New:
/static/css/main.v2.css
or /static/css/main.css?version=2- Let the browser use the cached version
As long as the URL is the same and the cache has not expired, the browser does not download the file again.
A simple example of versioned static file URLs in a template:
<link rel="stylesheet" href="/static/css/main.css?v=3">
<script src="/static/js/app.js?v=1"></script>You can automate versioning during build steps, but the important concept is:
If you enable long caching for static files, you must change the URL when the file content changes.
Static Files and Security
Serving static files sounds simple, but there are important security issues.
Directory Traversal
You must prevent users from accessing files outside the static directory.
Unsafe example:
- Static directory:
/app/static/ - User requests:
/static/../app.py
If the server incorrectly converts that to /app/app.py, the user could download your source code.
Secure static file servers always:
- Normalize the path
- Ensure the final path is still under the allowed folder
So a safe server will reject or sanitize paths like:
/static/../secret.txt/static/../../etc/passwd
Access to Sensitive Files
Never put secrets in your static folder:
.env- Database configuration
- Private keys
- Internal documentation
Anything in the static folder should be considered public.
Limiting Upload Types
Sometimes static-like files come from users, for example profile pictures or uploaded documents. These topics are covered in the file upload chapters, but keep in mind:
- Treat user uploads very differently from your own static assets.
- Often you will store uploads in a different folder and may use different servers.
Example: Simple Routing for Static Files
Imagine you are building a very tiny framework. You have a route for home and a route to serve static files.
Pseudo code:
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
STATIC_DIR = os.path.join(os.path.dirname(__file__), "static")
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path.startswith("/static/"):
return self.serve_static()
elif self.path == "/":
return self.serve_home()
else:
self.send_error(404, "Not Found")
def serve_home(self):
html = b"""
<html>
<head>
<link rel="stylesheet" href="/static/css/main.css">
</head>
<body>
<h1>Hello</h1>
<img src="/static/images/logo.png">
</body>
</html>
"""
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.send_header("Content-Length", str(len(html)))
self.end_headers()
self.wfile.write(html)
def serve_static(self):
# remove leading "/static/"
rel_path = self.path[len("/static/"):]
# build absolute path under STATIC_DIR
file_path = os.path.join(STATIC_DIR, rel_path)
# normalize to prevent "../" attacks
file_path = os.path.normpath(file_path)
# ensure file is still under STATIC_DIR
if not file_path.startswith(STATIC_DIR):
self.send_error(403, "Forbidden")
return
if not os.path.isfile(file_path):
self.send_error(404, "Not Found")
return
# simple content type detection
if file_path.endswith(".css"):
content_type = "text/css"
elif file_path.endswith(".js"):
content_type = "application/javascript"
elif file_path.endswith(".png"):
content_type = "image/png"
else:
content_type = "application/octet-stream"
with open(file_path, "rb") as f:
data = f.read()
self.send_response(200)
self.send_header("Content-Type", content_type)
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
if __name__ == "__main__":
server = HTTPServer(("0.0.0.0", 8000), MyHandler)
print("Serving on http://localhost:8000")
server.serve_forever()This is not production quality, but it illustrates:
- URL mapping to files
- Content type detection
- Simple security checks against directory traversal
Your real framework will provide something similar out of the box.
Static Files and Reverse Proxies
When you use a reverse proxy like Nginx, usual patterns are:
- Map
/static/to a directory on the server - Proxy other URLs to the backend app
Simplified Nginx configuration example:
server {
listen 80;
server_name example.com;
# Serve static files directly
location /static/ {
alias /var/www/myapp/static/;
# Optional caching headers
expires 30d;
add_header Cache-Control "public";
}
# Proxy all other requests to the app
location / {
proxy_pass http://127.0.0.1:8000;
}
}The key ideas:
location /static/is handled by Nginx, not by your application.alias /var/www/myapp/static/tells Nginx where on disk to find/static/....
This division of work is important in real deployments, but as a beginner you can focus on understanding what static files are and how URLs map to them.
Good Practices for Static Files
To finish, here are practical rules you can follow.
Static files rules to remember:
- Keep all public static assets in a dedicated directory, for example
static/. - Use clear URL prefixes such as
/static/or/assets/. - Never store secrets or private data in your static folder.
- Avoid user uploads in the same folder as your own static files.
- Use caching for static assets and change URLs when files change.
- Let a web server or reverse proxy handle static files in production when possible.
Following these rules keeps your backend simpler, faster, and more secure while making it easier to work with templates and frontend code.
Views: 9
KAHIBARO