KAHIBARO
Discord Login Register

Static Files

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:

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:

text
my_project/
    app/
        routes.py
        templates/
            base.html
            index.html
    static/
        css/
            main.css
        js/
            app.js
        images/
            logo.png
            banner.jpg

Common conventions:

TypeTypical FolderExamples
Stylesheetsstatic/cssmain.css, reset.css
JavaScriptstatic/jsapp.js, analytics.js
Imagesstatic/imageslogo.png, avatar.jpg
Fontsstatic/fontsRoboto.woff2, icons.ttf
Other assetsstatic/robots.txt, favicon.ico

The exact layout depends on the framework, but the pattern is always:

Static Files vs Templates

Templates are covered in their own chapter, but it is important to understand how static files relate to them.

Example HTML template that uses a static CSS file and a static image:

html
<!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:

How Static File Serving Works

At a high level, serving a static file follows this flow:

  1. Client requests a URL
    Example: GET /static/css/main.css HTTP/1.1
  2. Server maps the URL to a file path
    Example mapping rule:
    • URL prefix /static/
    • Filesystem directory /app/static/
      So /static/css/main.css becomes /app/static/css/main.css
  3. Server checks if file exists and is allowed
    • If yes, it reads the file and sends it
    • If no, it returns 404 Not Found
  4. Server sets headers such as:
    • Content-Type: text/css
    • Content-Length: 1234
    • Maybe caching headers like Cache-Control
  5. 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:

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:

python
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:

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:

So this mapping is possible:

URLFile path on disk
/assets/css/main.cssstatic/css/main.css
/assets/js/app.jsstatic/js/app.js
/assets/logo.pngstatic/images/logo.png

You control this mapping through configuration.

Why change the URL prefix?

Static Files During Development vs Production

In development you might let your framework serve static files directly because it is simple.

Example pattern:

In production, a common pattern is:

A typical production setup:

PartResponsibility
Nginx (or similar)Serve /static/, /media/, handle HTTPS
Backend appServe /api/*, /login, other dynamic routes

The idea is that web servers like Nginx are very good at:

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:

  1. Set long cache lifetimes for static files
    For example:
http
   Cache-Control: public, max-age=31536000

This tells the browser it can cache the file for a year.

  1. 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
  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:

html
<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:

If the server incorrectly converts that to /app/app.py, the user could download your source code.

Secure static file servers always:

So a safe server will reject or sanitize paths like:

Access to Sensitive Files

Never put secrets in your static folder:

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:

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:

python
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:

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:

  1. Map /static/ to a directory on the server
  2. Proxy other URLs to the backend app

Simplified Nginx configuration example:

nginx
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:

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:

  1. Keep all public static assets in a dedicated directory, for example static/.
  2. Use clear URL prefixes such as /static/ or /assets/.
  3. Never store secrets or private data in your static folder.
  4. Avoid user uploads in the same folder as your own static files.
  5. Use caching for static assets and change URLs when files change.
  6. 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

Comments

Please login to add a comment.

Don't have an account? Register now!