How to Set Up Server-Side Includes (SSI) in Apache

How to set up server-side includes (SSI) in Apache

Long before I ever touched a JavaScript framework, I was maintaining a static HTML site where every page repeated the same header and footer by hand. The day I discovered Server-Side Includes, I stopped copy-pasting navigation markup across dozens of files and let Apache do the assembling for me. It’s an old technology, but I still reach for it on small static sites where a full build pipeline would be overkill.

In this post I’ll cover what SSI is, why I still use it, and exactly how I configure it in Apache.

What Is Server-Side Includes (SSI)?

SSI is a simple interpreted language embedded directly inside HTML comments that Apache processes before it sends the page to the browser. A directive looks like this:

<!--#include virtual="/includes/header.html" -->

When Apache serves the file, it swaps that directive for the actual content of header.html — the browser never sees the directive at all, only the final assembled HTML.

Why I Still Use SSI Today

  • Static site reuse: sharing a common header/footer across dozens of static pages without a build step.
  • Dynamic snippets: showing the current date, a visitor’s IP, or a file’s last-modified time.
  • Lightweight conditionals: showing/hiding content blocks without spinning up a full application server.
  • Legacy maintenance: some older sites I’ve inherited rely on it, so understanding it is part of the job.

Prerequisites

  • Apache HTTP Server installed and running
  • Root or sudo access to edit config
  • Basic HTML familiarity

Step 1: Enable the Include Module

SSI processing runs through mod_include.

Debian/Ubuntu:

sudo a2enmod include
sudo systemctl restart apache2

RHEL/CentOS: I confirm the module is loaded in httpd.conf:

LoadModule include_module modules/mod_include.so

Step 2: Enable SSI Processing for a Directory

I edit the virtual host or directory block to allow SSI execution:

<Directory /var/www/html>
    Options +Includes
    AddType text/html .shtml
    AddOutputFilter INCLUDES .shtml
</Directory>
  • Options +Includes enables SSI processing in this directory.
  • AddType text/html .shtml tells Apache to treat .shtml files as HTML.
  • AddOutputFilter INCLUDES .shtml routes .shtml files through the SSI parser before they go out.

I reload Apache after changes:

sudo apachectl configtest
sudo systemctl reload apache2

Optional: Processing .html Files as SSI

If I want existing .html files to support SSI without renaming everything to .shtml (there’s a performance trade-off I get into below):

AddOutputFilter INCLUDES .html

Step 3: My First SSI File

/var/www/html/includes/header.html:

<header>
    <h1>My Static Site</h1>
    <nav><a href="/">Home</a> | <a href="/about.shtml">About</a></nav>
</header>

/var/www/html/index.shtml:

<!DOCTYPE html>
<html>
<head><title>Home</title></head>
<body>
    <!--#include virtual="/includes/header.html" -->
    <p>Welcome to my site. Today's date is <!--#echo var="DATE_LOCAL" --></p>
    <!--#include virtual="/includes/footer.html" -->
</body>
</html>

Load the page in a browser and the header and footer show up as if they’d always been part of the file.

Common SSI Directives I Use

Include a file

<!--#include virtual="/includes/footer.html" -->
<!--#include file="footer.html" -->

virtual is relative to the document root; file is relative to the current file’s directory. I stick with virtual for portability.

Echo a variable

<!--#echo var="DATE_LOCAL" -->
<!--#echo var="LAST_MODIFIED" -->
<!--#echo var="REMOTE_ADDR" -->

Set a variable

<!--#set var="name" value="Ahmad" -->
<p>Hello, <!--#echo var="name" -->!</p>

Execute a command (I use this sparingly and carefully)

<!--#exec cmd="uptime" -->

Conditional logic

<!--#if expr="$QUERY_STRING = /admin/" -->
    <p>Admin view</p>
<!--#else -->
    <p>Public view</p>
<!--#endif -->

Show file modification time

<!--#flastmod virtual="index.shtml" -->

Show file size

<!--#fsize virtual="/downloads/manual.pdf" -->

Real-World Use Cases

  • A documentation site with dozens of static pages sharing one navigation bar.
  • An internal status dashboard showing live command output, locked down carefully.
  • A small-business site maintained by non-developers editing raw HTML, relying on SSI for consistent headers/footers.
  • Legacy intranet applications that predate modern templating.

Security Best Practices I Follow

SSI’s exec directive is powerful, and I treat it with real caution. If a directory allows both file uploads and SSI exec, someone who uploads a .shtml file could run arbitrary shell commands on the server.

  • I disable exec unless I genuinely need it, using IncludesNOEXEC instead of Includes: Options +IncludesNOEXEC This still permits #include, #echo, #set, and #if, but blocks #exec.
  • I never enable SSI processing in directories where users can upload files.
  • I avoid exposing sensitive environment variables via #echo var="...".
  • I keep SSI-enabled directories entirely separate from upload directories as an extra layer of protection.

Performance Optimization

  • I prefer .shtml over blanket .html processing. Applying AddOutputFilter INCLUDES to every .html file forces Apache to parse each one for SSI directives, even files with none — a distinct extension limits that parsing to files that actually need it.
  • I add caching headers (mod_expires) for static assets referenced inside SSI pages.
  • I avoid #exec on high-traffic pages, since spawning a subprocess per request gets expensive fast.
  • For high-traffic sites, I’ve migrated from SSI to a static site generator that pre-renders includes at build time instead of on every request.

Mistakes I’ve Made

  • Forgetting AddOutputFilter INCLUDES and wondering why <!--#include --> shows up literally on the page.
  • Using file= with an absolute path when only virtual= reliably supports document-root-relative paths.
  • Leaving #exec enabled on a publicly writable directory.
  • Skipping apachectl configtest before reloading, which broke the server once on a syntax error I didn’t catch.
  • Expecting SSI to support full programming logic — it’s intentionally minimal and I don’t lean on it for anything complex.

Troubleshooting

SSI directives appear as literal text in the browser I check that mod_include is enabled and AddOutputFilter INCLUDES targets the right file extension.

500 Internal Server Error on SSI pages I check the error log:

sudo tail -f /var/log/apache2/error.log

Usually a referenced include file doesn’t exist, or exec is disabled but being called.

Includes not updating after editing the source file I clear any browser or intermediate proxy cache; SSI itself re-processes on every request by default.

FAQs

Is SSI still relevant in 2026? For small static sites and legacy systems, yes, in my experience. For anything beyond simple content reuse, I reach for a static site generator or a real backend framework instead.

Can SSI replace a templating engine like PHP or a JS framework? No. It’s intentionally limited to basic includes, variable substitution, and simple conditionals — no loops, no complex data structures, no database connectivity.

Does SSI hurt page load performance for visitors? Minimal for small includes, but every SSI-enabled request needs server-side processing rather than being served as pure static content, which is slightly slower at scale.

Summary and Key Takeaways

  • SSI lets Apache assemble HTML pages from reusable fragments at request time, using simple embedded directives.
  • I enable it with mod_include, Options +Includes, and AddOutputFilter INCLUDES scoped to a specific extension like .shtml.
  • I use IncludesNOEXEC instead of Includes unless I genuinely need command execution — #exec is a real security risk.
  • SSI still earns its place on small static sites, but I don’t treat it as a substitute for modern templating at scale.

References

  • Apache mod_include Documentation: https://httpd.apache.org/docs/current/mod/mod_include.html
  • Apache SSI Tutorial: https://httpd.apache.org/docs/current/howto/ssi.html
  • Apache Security Tips: https://httpd.apache.org/docs/current/misc/security_tips.html
Total
1
Shares

Leave a Reply

Previous Post
How to configure Apache to use a different log file format

How to Configure Apache to Use a Different Log File Format

Next Post
How to secure your Apache Web Server

How to Secure Your Apache Web Server

Related Posts