Prepending PHP to a Page in Gatsby

It goes against the entire Jamstack philosophy to add reliance to a back‑end technology like PHP, and certainly the Gatsby Content Mesh advocates against it, but sometimes there's simply no alternative.
Often, these server‑side scripts can be set up as standalone endpoints which you can then call via HTTP (for example ‑ by using axios). This is a really common approach for one‑way communication, like submitting a contact form. In Gatsby, if you are confident that your eventual hosting environment supports PHP (for example) you can even add these files into your static folder, safe in the knowledge that they will then be placed in, and be available at, the root of your domain.
However, there are times when this isn't sufficient. A recent project I worked on required that every page load be hydrated with up‑to‑the‑minute live pricing data, acquired from a monolith system built in PHP, where security policies made it impossible to use a more familiar, distributed approach. You might argue: possibly not the best use of a static site generator!

So, the answer in this instance was a PHP include, prepended to each generated page at build time. It produced an object on window for the React components to read when the page loaded.
Another use for me has been to prepend a small piece of PHP to my error page. This lets me record errors on the server and respond to patterns in those requests, including requests from visitors or bots that do not run JavaScript.
This workaround assumes Apache hosting where .htaccess overrides are enabled and the configured PHP handler accepts the mapping below. PHP‑FPM and other server setups may require different handler configuration, so check that with the host first. A purely static host cannot execute this PHP.
Use .htaccess to Allow PHP Processing
The html files generated by Gatsby are not ‑ on their own ‑ going to trigger any PHP you attempt to push into them without a little help.
If you don't already have an htaccess file in your project, create one (important: the file name is .htaccess ‑ there is nothing before the extension, your OS is probably going to hide it immediately). This goes into your static folder and will then be copied into your site root on build.
Then, there are two options in the code block below to use as best suits your needs:
- The first will mean that all files with the
.htmlextension should be parsed as PHP. I don't recommend this unless you really and genuinely are relying on the back‑end for every page (in which case, maybe Gatsby isn't the answer). - The second does the same but only applies to a specific file ‑ in this case
404.html.
AddType application/x-httpd-php .html
<Files 404.html>
AddType application/x-httpd-php .html
</Files>Install prepend-file
prepend-file is a simple package which just prepends text to a file within your generated site. We will use this as part of our deployment script. So, add to your project by simply running yarn add prepend-file from the project root (unless you're using NPM, in which case it's npm install prepend-file).
Write Your Prepend Script
This is a very similar process as I've described before where we want to add additional Node.js functionality within our build process. Create a file called prepend.js in your folder root, import prepend-file, and configure it to add an include to your PHP file to the file you want to add PHP to.
This is how I capture hits on my error page (with some comments to explain what's going on). Note that I'm also using my LIVE_DEPLOY environment variable here to determine where the build is occurring.
// import our environment variables
require('dotenv').config({ path: '.env' });
// if our LIVE_DEPLOY variable isn't set, or is not set
// to 'true', then we return without going any further
if (process.env.LIVE_DEPLOY !== 'true') {
console.warn('Deploy env is false, not prepending');
return;
}
// import prepend-file
var prependFile = require('prepend-file');
// open the 404 file, and inject an include for
// include.php
prependFile('./public/404.html', "<?php include_once('include.php'); ?>", (error) => {
if (error) {
console.error('Could not prepend the error file:', error);
process.exitCode = 1;
return;
}
console.log('Error file prepended successfully');
});The script opens the generated public/404.html and prepends this PHP statement:
<?php include_once('include.php'); ?>
Put include.php in the static folder so it is copied into the deployed output. The include statement sits at the top of 404.html; the PHP handler executes it when serving the request. The full <?php opening tag avoids relying on the host enabling short tags.
Add Prepend to Your Build Script
Much like setting up Gatsby to deploy via FTP, one final task is to add your new prepend script into your build process and much of this will come down to your own personal preference.
Inside my package.json, I like to have a specific 'deploy' script which is only called (via yarn deploy) within the pipeline I have configured to push content out onto the live site. Mine looks like this:
"scripts": {
"deploy": "gatsby clean && gatsby build && node prepend && node deploy"
}In order, this chains four commands together:
gatsby clean‑ make sure we have cleared out any cached items we don't need;gatsby build‑ build the Gatsby project;node prepend‑ calls our newprepend.jsfile which in turn adds a PHP include to our404.htmlfile;node deploy‑ callsdeploy.jswhich takes a copy of the contents of thepublicfolder, and uploads it via FTP. I've written about this here.
To try the script locally, run node prepend against an existing generated file in public, with LIVE_DEPLOY set to true. Check the file starts with the include. If prepending fails, the script reports the error and exits with a non‑zero status, so the && chain stops before node deploy.
Postscript
June 2026: this was a pragmatic workaround for a Gatsby site tied to a PHP‑backed estate. I'm keeping it because that kind of compromise still appears in migrations, but for new work I would usually separate the integration properly or use the issue as a signal for Gatsby‑to‑Next.js migration planning.