Jan's Development Microblog
Go back to blog overview

How to create static websites with .htaccess

How I made my website 400% faster by serving static HTML using the .htaccess file.

A few days ago, I was looking for a way to improve my website's loading speed even further. One idea that came to mind was static file serving. In other words, saving a copy of the rendered page as a static HTML file on the server and using that file the next time the page is accessed, instead of re-rendering it.

I’ve done this many times in the past but always used a PHP layer to serve those static pages. This time, I didn’t want to use PHP to serve them. Why create unnecessary overhead when you can use a lower-level solution? Besides, the less code you need, the faster your website runs. Most websites and applications are overly complex and hard to maintain due to the use of frameworks and third-party libraries.

While adding this to my system, I took some timing measurements, and the render time went down from 20ms to 4ms. That's a significant reduction. Of course, the render time is just a small part of the total page loading time, but every millisecond counts.

The setup​

For my own blogging platform, I wanted to see if I could use .htaccess to detect whether an HTML file exists. If the file exists, it should be served to the client. If the file doesn't exist, the page can be rendered using PHP. After asking ChatGPT for some ideas, it turns out this is possible and not too difficult to set up. However, there are three things required to get this working.​

  1. setup .htaccess-file
  2. code to create static HTML files
  3. code to delete static HTML files

The first thing we need to do is create a folder where the static HTML files will be stored. For my environment, I chose /caching/static/.​

Setup .htaccess

Editing a .htaccess file can be a hassle, so make backups before changing any lines. One mistake, and your website could go offline. But enough of those warnings! I added the code below to my .htaccess file.

# Check if the request is for the root directory
# Check if the static/index.html file exists and serve it
RewriteCond %{REQUEST_URI} ^/$
RewriteCond %{DOCUMENT_ROOT}/static/index.html -f
RewriteRule ^$ /static/index.html [L]

# check if the file exists in /caching/static/ and serve it
RewriteCond %{DOCUMENT_ROOT}/caching/static/$1.html -f
RewriteRule ^(.*)$ /caching/static/$1.html [L]

# when not found, use the default way
RewriteCond %{DOCUMENT_ROOT}/$1.html !-f
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*) index.php?path=$1 [L,QSA]

The code above consists of three parts.

The first part checks if the user accesses the root of the website. In that case, the server will look for an index.html file in the /caching/static folder. If the file exists, it will be served to the user. If not, the script will ignore this rule and fall back to its default behavior.

The same applies to the second part, with the difference being that it checks the second part of the URL. For example, if the URL is domain.com/static-file, only the static-file part is used. The server then checks if there is a file named static-file.html in the /caching/static folder. If the file exists, it will be served to the user. If not, the script will ignore this rule and fall back to its default behavior.

The third and final part of the code in the .htaccess file is a method for creating friendly URLs. I included these lines only for clarity within this article.

Create static HTML files

The second step in creating static HTML files using .htaccess is to generate the static HTML files themselves. To achieve this, you need to include a few lines of PHP code at the beginning and the end of the page. This is necessary to capture the rendered HTML and save it as a static HTML file on the server.

Add the ob_start() function at the top of your script. The ob_start() function initiates output buffering, which means subsequent output is stored in an internal buffer rather than being sent directly to the browser. This enables tasks such as modifying headers after content generation, capturing output for later use, or compressing content before delivery.

ob_start();

At the end of your script, include the method to store the output buffer in a file. You should use the code below for this. I’ve included an extra check to detect if the static_cache is enabled. It's totally optional.

​​if (defined('STATIC_CACHE') && STATIC_CACHE) {
$renderedContent = ob_get_contents();
    $filename = __DIR__ . '/caching/static/' . $_GET['path'] . '.html';
file_put_contents($filename, $renderedContent);
}

Delete static HTML files​

This might be the most important part of the script. Creating static HTML files is one thing, but there is no way to update those files once they are generated on the fly. So, we need to include some code to delete the static HTML files. For my project, I needed to add the code below. I have added this script in the method that updates the blog article within my website. You might want to adjust this so it works with your own project code.

$urlsToDelete = [
__DIR__ . '/../caching/static/' . $_POST['url'] . '.html',
__DIR__ . '/../static/caching/index.html'
];

foreach ($urlsToDelete as $cacheFile) {
if (file_exists($cacheFile)) {
unlink($cacheFile);
}
}​​

Final words

While this seems to work like a charm and fits perfectly within my code, it might not suit your project. However, I hope you gained new insight into optimizing your own PHP project code.