I can't figure out why the CSS sheet called in my base template's header isn't hooked up when I check my index.html file on http.server
. I'm migrating my site to Python and Jinja, no frameworks.
My server module/script:
# server.py
import os
import http.server
import socketserver
from jinja2 import Environment, FileSystemLoader
PORT = 8000
STATIC_DIR = "static/"
class StaticFileHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=STATIC_DIR, **kwargs)
with socketserver.TCPServer(("", PORT), StaticFileHandler) as httpd:
print(f"Serving at http://localhost:{PORT}/")
httpd.serve_forever()
The base template's header (omitting the rest of the template):
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<title>{% block title %}{% endblock title %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noai, noimageai, noarchive">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.css">
</head>
I've tried writing the paths to the static files as "static/styles.css"
, but no luck there. What am I missing? In server.py, what do I enter for STATIC_DIR
if I want the project's home folder to be the static directory? Leaving it blank or adding /
show my computer's root directory, not the project's.
How would I set up server.py so it pulls static files like CSS, fonts, etc. from STATIC_DIR
, but looks for index.html from a different directory like the project folder, or a "site" folder?
My test script for generating index.html saves the HTML file to the project folder (I drag it to "static" for testing):
# write-posts.py
from jinja2 import Environment, FileSystemLoader
from datetime import datetime, timezone
# date = datetime(tzinfo=timezone.utc).isoformat()
posts = [
{"title": "First Post", "slug": "first-post", "date": "2025, 7, 1", "body": "First post!", "tags": "First Post"},
]
environment = Environment(loader=FileSystemLoader("templates"))
template = environment.get_template("index.html")
filename = "index.html"
content = template.render(posts=posts)
with open(filename, mode="w", encoding="utf-8") as message:
message.write(content)
print(f"... wrote {filename}")
I tried Furas's suggestions, but I'm still not getting the static files to connect:
server.py:
import os
import http.server
import socketserver
from jinja2 import Environment, FileSystemLoader
PORT = 8000
BASE_DIR = "source/" # Directory containing static files. "BASE_DIR="/full/path/to/www/" might be better if server runs from a different working directory.
class StaticFileHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=BASE_DIR, **kwargs)
with socketserver.TCPServer(("", PORT), StaticFileHandler) as httpd:
print(f"Serving at http://localhost:{PORT}/")
httpd.serve_forever()
base.html template (head tag only):
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<title>{% block title %}{% endblock title %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noai, noimageai, noarchive">
<link rel="icon" type="image/x-icon" href="static/favicon.ico">
<link rel="stylesheet" href="static/styles.css">
</head>
write-posts.py (generating index.html, I also have a template for my homepage titled "index.html"):
from jinja2 import Environment, FileSystemLoader
from datetime import datetime, timezone
# date = datetime(tzinfo=timezone.utc).isoformat()
posts = [
{"title": "First Post", "slug": "first-post", "date": "2025, 7, 1", "body": "First post!", "tags": "First Post"},
]
environment = Environment(loader=FileSystemLoader("source/templates"))
template = environment.get_template("index.html")
filename = "index.html"
In my CSS file paths to static files are written like:
src: url("static/fonts/font-example.woff2") format("woff2");
Messages in the terminal when I load the page in my browser:
127.0.0.1 - - [18/Jul/2025 10:22:56] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [18/Jul/2025 10:23:05] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [18/Jul/2025 10:23:05] code 404, message File not found
127.0.0.1 - - [18/Jul/2025 10:23:05] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [18/Jul/2025 10:29:44] "GET / HTTP/1.1" 304 -
In my browser's console (Firefox) I only see a 404 message for the favicon file.
My project structure:
project folder
-- server.py (`http.server` module/script)
-- write-posts.py (test script for generating index.html from Jinja templates and static files)
-- venv folder (virtual environment folder)
-- source folder (for HTML, static, and template files, an "input" folder)
-- index.html (what write-posts.py generates, I manually move it from project to source and overwrite the previous version)
-- static folder
-- static files like styles.css, favicon.ico, logo.png, paper.webp (for wallpaper)
-- fonts folder
-- templates folder
-- base.html (base template)
-- homepage.html (child template of base, template for the homepage. previously called "index.html" but renamed to avoid confusion)
server.py:
import os
import http.server
import socketserver
from jinja2 import Environment, FileSystemLoader
PORT = 8000
BASE_DIR = "source/"
class StaticFileHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=BASE_DIR, **kwargs)
with socketserver.TCPServer(("", PORT), StaticFileHandler) as httpd:
print(f"Serving at http://localhost:{PORT}/")
httpd.serve_forever()
write-posts.py:
from jinja2 import Environment, FileSystemLoader
from datetime import datetime, timezone
# date = datetime(tzinfo=timezone.utc).isoformat()
posts = [
{"title": "First Post", "slug": "first-post", "date": "2025, 7, 1", "body": "First post!", "tags": "First Post"},
]
environment = Environment(loader=FileSystemLoader("source/templates"))
template = environment.get_template("homepage.html")
filename = "index.html"
content = template.render(posts=posts)
with open(filename, mode="w", encoding="utf-8") as message:
message.write(content)
print(f"... wrote {filename}")
base.html:
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<title>{% block title %}{% endblock title %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noai, noimageai, noarchive">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.css">
<link rel="alternate" type="application/feed+json" href="/rss.json"/>
</head>
<body>
<header>
<a href="{% url 'home' %}" class="/static/logo"><img src="logo.png" alt="TBD"></a>
In write-posts.py it saves index.html to the "project" folder. Since server.py looks at the source folder for files, I manually drag index.html from project to source. I have tried these file paths in base.html and styles.css yet none work:
project/source/static/<file-name>
/project/source/static/<file-name>
/source/static/<file-name>
source/static/<file-name>
/static/<file-name>
static/<file-name>
/<file-name>
<file-name>
I think name of variable STATIC_DIR
is missleading and it can confuse with folder name static
.
As for me it should have name BASE_DIR
and you could have folder www/
or html/
like in real WWW servers.
So your structure could be
project/
+-> server.py
+-> www/
+-> index.html
+-> static/
+-> style.css
+-> script.js
+-> posts/
+-> 1.html
+-> 2.html
And code could use BASE_DIR = "www/"
or even better BASE_DIR="/full/path/to/www/"
because you may run server from different current working directory
.
(frankly, even name StaticFileHandler
can be missleading because it is handler for all files on server, not only in folder static
. To server only static files it would need something more complex, and it would need something else to serve other files - like .html
)
server.py
import os
import http.server
import socketserver
from jinja2 import Environment, FileSystemLoader
PORT = 8000
BASE_DIR = "www/"
class StaticFileHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=BASE_DIR, **kwargs)
with socketserver.TCPServer(("", PORT), StaticFileHandler) as httpd:
print(f"Serving at http://localhost:{PORT}/")
httpd.serve_forever()
And html could use static/style.css
or even better with leading /
like /static/style.css
if you would like to use it also in html in subfolders like posts/1.html
index.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="/static/style.css">
</head>
<body>
<a href="/posts/1.html">Post 1</a><br/>
<a href="/posts/2.html">Post 2</a><br/>
</body>
</html>