We rebuilt our own website with no database and no login page
For most of our life as a firm, astriscsp.com ran on WordPress. It worked well enough. It was also a live application on the public internet, with an administrator login, a database behind it, a page builder and a set of plugins that each needed updating on somebody else’s schedule.
We spend our days telling clients to reduce the number of things that can be attacked. Our own website was not a good example of that advice.
That was the first reason and the one that started it. Two others turned out to matter as much: how long it took us to get a thought published, and how little the old site said about who we actually work for.
What a content management system is, from a security point of view
It helps to be clear about what you are running. A CMS-driven site is software. It runs on every request, builds each page out of a database, and puts an administrative login on the public internet for anyone who finds the URL.
That leaves you with a standing job. You patch the platform. You patch every plugin when its author releases something, not when it suits you. You defend a login page that sits on a well-known path and gets probed all day by automated traffic. And you carry the risk of the supply chain behind each plugin, which most site owners have never really looked at.
This is not a complaint about WordPress. It is the price of the architecture, and it is a fair price if you are using what it offers. Our site changes a few times a month and nobody logs into it but us. We were paying for capability we did not use.
What we moved to
The site is now built with Astro and published to Cloudflare Pages as static files.
There is no database. There is no application running on a server. There is no admin interface, because there is nothing to administer. The pages are built on our own machines and the finished files are uploaded. Somebody asks for a page and gets it.
So the usual routes into a website are closed. You cannot attack a database that does not exist, or compromise a plugin we never installed, or steal the password to a login page we never deployed. We did not defend against those attacks. We removed the things they aim at.
The site is faster and cheaper to run too, though we would rather not oversell that. Serving a file costs less than building a page on demand, but at this size the saving is small, and it would not have been reason enough on its own.
What is left, we set up deliberately. This is the live configuration, not an example:
Content-Security-Policy: default-src 'self'; img-src 'self' data:;
font-src 'self'; frame-ancestors 'none'; object-src 'none'
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Permissions-Policy: geolocation=(), microphone=(), camera=()
The first line is the one that matters. default-src 'self' tells the browser to load nothing from anywhere except our own domain, and font-src 'self' means the fonts come from us rather than a third-party CDN. Reading this page involves no request to anyone else. The last line switches off browser features the site has no use for, so a page here cannot ask for your location, microphone or camera even if something managed to inject the code that would.
Someone told us, with some amusement, that we had forgotten the cookie banner. We had not. The site sets no cookies, so there is nothing to ask consent for. No advertising tags, no tracking pixels, no third-party scripts. We do count how many people read each page, using a method that sets nothing in your browser and identifies nobody.
It is worth being clear about why, because a lot of people now treat the banner as the requirement. The banner is what you show when you set cookies that need consent. Most sites need one because they carry a stack of third-party tags: analytics, advertising, an embedded video, a chat widget. Each of those is another company receiving data about your visitors, and another script running in their browser. We took the tags off, so the banner went with them.
Why GitHub is the part that made it work
Taking away the CMS takes away the place you type. Git is what replaced it.
Every change to the site is a commit: a price, a paragraph, a new journal entry, a design decision. Each one records who made it, when, and why. Anything substantial goes onto a branch and through a pull request before it reaches the live site, and merging that branch is what publishes it.
Two things come out of that. There is a full history of what the site has said over time, which matters for a firm that publishes claims about its own services. And undoing a change is one command rather than a restore from backup.
The content itself is plain Markdown files sitting in the repository. This post is a text file, and it starts like this:
---
title: "We rebuilt our own website with no database and no login page"
date: 2026-07-26
kicker: "Astris · Practice · 2026"
description: "Our site used to run on WordPress..."
---
Then the article, written as ordinary text. That is the whole format. Nothing is locked inside a database export, nothing needs the platform to stay readable, and anyone who can open a text file can read it in ten years.
Doing it without an agency
The other thing that changed is who does the work.
Any real change to the old site meant going to a third party. That costs money, it takes time, and for a security firm it means another organisation holding access to our web presence.
It also meant something less obvious. An agency does not only build the site. It decides how you are described, which is the more valuable half of the job and the half firms tend to hand over without noticing. The coder was the visible cost. The positioning was the part that mattered, and we had been renting it.
We now build and maintain the site ourselves, using Claude to do the work. It writes and edits the components, converts content and handles the front-end work we used to send out. What the site claims, what we are willing to publish and whether a change is right stay with us. We would not hand those to a model, and a person still reads every change before it merges.
The saving that matters is not the invoice. It is the gap between wanting something changed and the change being live.
That gap used to be a fortnight. You write the request, somebody scopes it, it waits behind other work, it comes back not quite right, and the correction takes another round. By the time a price is updated the reason for updating it has moved on. Small improvements never happen at all, because nobody starts a two-week process to fix one paragraph. Now the gap is a few hours, and a correction is a few minutes.
What is worth noticing is the thing that did not get worse. Moving that fast normally costs you consistency, because whoever is moving fast is rarely the person holding the house style, the quality bar and the security of the platform in their head. Here none of that depends on anyone remembering it. The conventions and the voice are written down in the repository, so every draft starts from them. Every change is read by a person before it publishes, and read as the finished page rather than as a list of edits. The security of the site is a property of how it is built, so there is no database to expose and no plugin to leave unpatched however quickly we work.
Speed and control usually pull against each other. They stopped pulling against each other here because the controls are built into the thing rather than added as steps in a process.
We should be careful about what this proves. The migration was directed by someone with a background in software engineering, large-scale web platforms, deployment pipelines and security. That background did real work here: choosing a static build in the first place, knowing what belongs in a content security policy, exporting the DNS zone before touching anything, and being able to spot when the model has produced something wrong.
That last one is the important one. Ask for something the model has misunderstood and it will hand you convincing code for the wrong thing, with no sign that anything is off. If nobody on your side can read the result and judge it, you have not taken control of your website. You have replaced an agency you could hold to account with a tool you cannot. What actually changed for us is narrower than it sounds: the model took away the friction that made it easier to pay someone else to do work we could already specify and check.
It also gave us the design back. The typography and layout here are chosen, rather than whatever the page builder templates allowed.
What rebuilding made us decide
A migration sounds like a technical job. It is not, or not only. You cannot move a page across without deciding what the page is for, and doing that to every page in turn asks harder questions than the technical work does.
We had to write down plainly who we work for: regulated wealth-management firms, family offices, trust companies and law firms, usually with small internal teams and obligations in more than one jurisdiction. Once that was on paper, some of what the old site said stopped making sense. It was there because it is the sort of thing firms like ours say, not because it told a reader anything they needed.
The journal changed the most. The posts we brought over were general industry pieces on technology trends and emerging threats, the kind that could have sat on any managed service provider’s website without anyone noticing the difference. What we publish now comes out of work we have actually done: an incident we handled and what we decided afterwards, a regulatory deadline that lands in one jurisdiction and not another, a benchmark drawn from the environments we run. This post is the same thing applied to ourselves.
That shift was not caused by the technology. Moving the site simply made it obvious, because you cannot rewrite a page without reading what it says.
The migration itself
The existing posts were exported and converted to Markdown by script rather than retyped, which brought the writing across without a decade of accumulated editor markup.
Before changing anything, we exported the DNS zone and kept a copy in the repository. That is the step people skip. DNS is the one part of a migration where a mistake is visible to everybody within minutes, and it is far less stressful to fix when you have the old settings written down.
One thing did go wrong, and it is worth recording. Our content security policy blocks inline scripts, which is the whole point of having it. The script that opens the menu on a phone was written inline, in the page:
<script>
document.querySelector('.nav-toggle').addEventListener('click', ...)
</script>
It worked perfectly on our own machines, where the policy is not applied. On the live site the browser refused to run it, and the menu button simply did nothing. No error a visitor would ever see. The fix was to move the same code into its own file and load it from there, which the policy allows because the file comes from our domain:
// public/menu.js
// Kept as an external, same-origin file so it satisfies the site's
// Content-Security-Policy (script-src 'self') — an inline script would
// be blocked. See public/_headers.
const toggle = document.querySelector('.nav-toggle');
That comment is still at the top of the file. It took a minute to fix and rather longer to learn from: a control that only applies in production will find your mistakes in production. Test against the real configuration, not the convenient one.
What we would take from it
Most business websites do not need a content management system. They need pages that rarely change and somewhere to put an article. If the only person logging in is the administrator, the login page is doing you no good at all.
Removing something beats defending it. You can harden a CMS a long way. You cannot make its database, its plugins or its admin panel stop existing. The strongest control available is usually not having the component.
Watch the gap between wanting a change and having it. For most firms it is weeks, which quietly decides that small improvements are not worth asking for. Shortening it is worth more than it sounds, and it does not have to cost you consistency if the standards live in the system rather than in whoever happens to be doing the work.
Rebuilding a site is a good excuse to re-read it. Most of the value we got was not technical. Deciding what each page was for, and who it was for, changed more about the site than the platform did.
Version control gives you a record you probably do not have today. Who changed what, when and why, with a review step and an easy way back. For any firm making regulated or contractual claims in public, that is worth having for its own sake.
AI makes this cheaper to do, not easier to understand. It closes the gap between deciding on a change and shipping it. It does nothing about the gap between shipping a change and knowing it was right. If no one on your side can check the output, you have not removed a dependency, only made it harder to see.
If you are showing a cookie banner, find out why. Something is setting cookies that need consent, usually third-party tags you inherited rather than chose. Going through them is an hour well spent, and a few can often go.
Test against the security settings you actually run. A control relaxed on your own machine is a control you have not tested. Ours failed quietly on our own website. On a client system, the same gap is an incident.
One clarification, since the subject invites it. We do not build or host websites for clients. This is an account of work we did on our own site, written up because the reasoning behind it transfers even though the service does not.