SSI vs CSI: Understanding Server-Side and Client-Side Includes

In web development, content reuse is a common challenge. Whether it’s a header, footer, navigation menu, or any repeated block of HTML, you don’t want to copy and paste the same code into every file. Instead, you'd want a system that can include shared content automatically.

This is where SSI (Server-Side Includes) and CSI (Client-Side Includes) come into play. Both are techniques for injecting reusable content, but they operate at different points in the rendering pipeline.


What is SSI (Server-Side Includes)?

Server-Side Includes (SSI) is a feature of web servers (like Apache or Nginx) that allows you to include the content of one file inside another on the server, before the page is sent to the user's browser.

How SSI Works

  • The server reads your HTML page.

  • When it finds an SSI directive (e.g., <!--#include file="header.html" -->), it replaces that tag with the contents of the specified file.

  • Then it sends the final page to the client.

Example (Apache):

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

This would include header.html from the /includes/ folder into the current page.

Configuration (Apache):

To use SSI with Apache:

Options +Includes
AddType text/html .shtml
AddOutputFilter INCLUDES .shtml

What is CSI (Client-Side Includes)?

Client-Side Includes (CSI) are similar in purpose β€” injecting reusable content β€” but the inclusion happens in the browser, after the page is loaded. Typically done with JavaScript (often using fetch() or XMLHttpRequest), CSI lets the client pull in content dynamically.

Example:

<div id="header"></div>
<script>
  fetch('/includes/header.html')
    .then(res => res.text())
    .then(html => {
      document.getElementById('header').innerHTML = html;
    });
</script>

Implementing SSI in Node.js (Express)

If you're building a server with Node.js and Express, and want to simulate SSI without importing external libraries or using a template engine, you can do this manually with basic file operations.

File Structure

project/
β”œβ”€β”€ views/
β”‚   β”œβ”€β”€ header.html
β”‚   β”œβ”€β”€ footer.html
β”‚   └── index.html       ← contains only main content
└── server.js

views/index.html

<h1>Welcome to My Website</h1>
<p>This is the main content of the homepage.</p>

server.js

const express = require('express');
const fs = require('fs');
const path = require('path');

const app = express();
const PORT = 3000;

function renderWithIncludes(filePath) {
  const rawHtml = fs.readFileSync(filePath, 'utf8');
  return rawHtml.replace(/<!--#include\s+file="(.+?)"\s*-->/g, (_, includePath) => {
    const fullIncludePath = path.join(path.dirname(filePath), includePath);
    if (fs.existsSync(fullIncludePath)) {
      return fs.readFileSync(fullIncludePath, 'utf8');
    }
    return `<!-- Missing include: ${includePath} -->`;
  });
}

app.get('/', (req, res) => {
  const mainPath = path.join(__dirname, 'views', 'index.html');
  const fullHtml = `
    <!DOCTYPE html>
    <html>
      <head><title>Home</title></head>
      <body>
        <!--#include file="header.html" -->
        ${fs.readFileSync(mainPath, 'utf8')}
        <!--#include file="footer.html" -->
      </body>
    </html>
  `;

  const finalHtml = renderWithIncludes(path.join(__dirname, 'views', 'temp.html'));
  res.send(finalHtml);
});

app.listen(PORT, () => {
  console.log(`Server is running at http://localhost:${PORT}`);
});

In this example, you can manually or programmatically build a temp.html page containing <!--#include file=... --> tags and then process it with renderWithIncludes().

Why Prefer SSI (Without Libraries or Web Components)?

There are several valid reasons why you might prefer using a custom SSI-like setup instead of bringing in a full templating engine (like EJS or Handlebars) or modern web components:

1. Simplicity

  • If your site is mostly static, and you just want to reuse headers and footers, you don’t need a heavy framework or build step.

  • A basic SSI-like file reader avoids complexity and dependencies.

2. Zero Dependencies

  • Pure Node.js + Express + fs module is often enough for simple websites.

  • Reduces install size, build steps, and attack surface (especially for internal tools or small projects).

3. Performance

  • With SSI, the server builds full HTML before sending it to the browser, leading to faster initial page loads and SEO-friendly output.

4. No JavaScript on Client

  • CSI requires JavaScript to run in the browser β€” which can fail for users with JS disabled or impact SEO.

  • SSI ensures everything is pre-rendered.

5. Avoiding Web Components

  • Not all browsers support Web Components fully without polyfills.

  • They often require JavaScript + build tooling (e.g., Lit, Stencil), which you might want to avoid for simple projects.


SSI vs CSI

FeatureSSI (Server-Side)CSI (Client-Side)
Execution PointOn the serverIn the browser
DependenciesNone (can use built-in Node.js)Requires JavaScript
SEO SupportFully crawlable (rendered server-side)May be ignored by bots without JS
PerformanceFaster first load, content pre-renderedMay appear slower due to extra HTTP calls
FlexibilityLimited to static file injectionCan use logic, conditions, APIs, etc.
Use CaseStatic content reuseDynamic content, personalization

When to Use SSI

Use Server-Side Includes when:

  • You’re working on a static or semi-static site

  • You want pre-rendered HTML for SEO

  • You don’t want to add libraries or frameworks

  • You want to reuse common layout fragments easily


When to Use CSI

Use Client-Side Includes when:

  • Your app is heavily dynamic or personalized

  • You already use client-side frameworks (React, Vue, etc.)

  • You're okay with extra HTTP calls and delayed rendering

  • SEO is not a concern (e.g., admin dashboards, internal apps)


Conclusion

Both SSI and CSI solve the same problem: HTML reuse. The best choice depends on your goals.

  • SSI is simple, fast, and SEO-friendly β€” perfect for lightweight or static sites.

  • CSI offers flexibility and dynamic capabilities but relies on JavaScript and client-side rendering.

For modern projects, you might use:

  • EJS, Handlebars, or Pug for server-side rendering

  • React or Vue for client-side rendering

  • Custom SSI (as shown above) when you want a no-dependency, no-framework approach

πŸ‘‹ Enjoyed this blog?

Reach out in the comments below or on LinkedIn to let me know what you think of it.

For more updates, do follow me here :)

0
Subscribe to my newsletter

Read articles from Aakanksha Bhende directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Aakanksha Bhende
Aakanksha Bhende

Software Engineer | Open Source Enthusiast | Mentor | Learner I love documenting stuff that I come across and find interesting. Hoping that you will love reading it and get to know something new :)