FlatPress Blog and CMS 1.5.1
Overview
FlatPress is an open source blog engine and small CMS that needs no database. Posts, static pages, comments, settings and the administrator account are plain files, so the whole blog is one folder that you can back up and move with ordinary file tools.
You write posts with a BBCode editor, add static pages such as About or Contact, moderate comments, arrange sidebar widgets and switch themes from a browser-based administration area.
This guide covers launching and using the FlatPress Blog and CMS 1.5.1 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud BookStack, CMS, Blogs and Wikis listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.
What you get:
- No setup wizard, no default login. Out of the box, FlatPress offers a web setup wizard that creates the administrator on the first visit to a new site. This image removes the wizard. Your instance's first boot sets FlatPress up before the web server starts, with an administrator password, a blog id and a hash salt generated on your instance. The password is recorded in
/root/flatpress-credentials.txt. Nothing from the image build ships in the image. - Security fixes released after 1.5.1, already applied. FlatPress 1.5.1 is the newest release. For its next release, the FlatPress project has fixed a comment and contact form script injection that an anonymous visitor could use, a way to read unpublished drafts through a crafted page address, and two BBCode script injection advisories (GHSA-26cp-8p3v-5f66 and GHSA-q7fj-xg8x-4qxx). This image applies those upstream fixes to 1.5.1, unchanged, and documents them in
/usr/share/doc/flatpress/MODIFICATIONS. - HTTPS from the first boot. nginx serves FlatPress on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
- Fail closed. nginx and PHP refuse to start until first boot has finished.
- The blog data folder is never served. FlatPress relies on Apache
.htaccessrules that nginx ignores, so this image replaces them. Thefp-contentfolder (settings, the administrator's password hash, the hash salt, entries, comments and caches) is never served. The exception is uploaded images and files, which are served as static files with a policy that stops an uploaded HTML or SVG file from running script. PHP runs only through FlatPress's own entry points. Library, theme, plugin and uploaded PHP files are never run directly. - Failed-login lockout. FlatPress itself only slows down repeated failures within one browser session. On this image, ten failed sign-ins from one address within 15 minutes block that address for five minutes. The address comes from the connection, never from a header a client sends. FlatPress records commenter addresses the same way.
- Hardened defaults. The FlatPress code is owned by root and read only to PHP; PHP can write only the
fp-contentfolder, cannot run shell commands and is confined to FlatPress's folders. nginx and PHP are blocked from the instance metadata service, and the AMI requires IMDSv2. Requests for a host name the instance does not serve are dropped. FlatPress's links always use the host name you configure, never the one a request claims. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: FlatPress 1.5.1 (the upstream release, checksum verified, with the security fixes above) with its Leggero theme and standard plugins, PHP 8.3 FPM, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. First boot creates a welcome post and the Menu, About and Privacy Policy static pages.
Default user: ubuntu. Architecture: x86_64. Recommended instance type: m5.large.
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route that you use for management).
- A security group that allows TCP 22 (SSH), TCP 443 (the blog and its administration area) and TCP 80 (redirects to HTTPS) from the networks that should reach them. A public blog needs 443 and 80 open to everyone. Keep 22 open to your own networks only.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick FlatPress Blog and CMS 1.5.1 on Ubuntu 24.04 as the software version and select your Region. Then choose Launch through EC2.
Choose m5.large, your key pair and a security group as described above. FlatPress is very light, so a smaller instance is fine for a small blog.
To launch with the AWS CLI instead, replace the values in angle brackets:
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=flatpress}]'Connect to your instance
For the FlatPress Blog and CMS 1.5.1 on Ubuntu 24.04 version, the SSH user is ubuntu. Connect with:
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot takes a few seconds. After that, nginx, PHP and the first boot unit are active:
systemctl is-active nginx php8.3-fpm flatpress-firstbootactive active activenginx serves ports 80 and 443. PHP listens on a local socket only, and there is no database, so the only open ports are 22, 80 and 443:
sudo ss -tlnp | awk 'NR==1 || /nginx|sshd|php/' | awk '{print $1, $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g'State Local Peer LISTEN 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) LISTEN 0.0.0.0:22 users:(("sshd"),("systemd")) LISTEN 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) LISTEN [::]:80 users:(("nginx"),("nginx"),("nginx")) LISTEN [::]:22 users:(("sshd"),("systemd")) LISTEN [::]:443 users:(("nginx"),("nginx"),("nginx"))grep -oE "define\('SYSTEM_VER', '[0-9.]+'\)" /var/www/flatpress/fp-includes/core/core.system.php php -v | head -1define('SYSTEM_VER', '1.5.1') PHP 8.3.6 (cli) (built: Oct 19 2026 15:16:58) (NTS)Get your sign-in details
The credentials file holds the address the blog is served as (your instance's public DNS name by default), the sign-in page, the administrator user name and the password generated on this instance. Display it with:
sudo cat /root/flatpress-credentials.txtFLATPRESS_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ FLATPRESS_LOGIN_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/login.php FLATPRESS_ADMIN_USER=admin FLATPRESS_ADMIN_PASSWORD=<24 letters and digits>Visit your blog and sign in
Open FLATPRESS_URL in your browser. The certificate is self-signed (generated on your instance), so accept the browser warning once, or install your own certificate (Step 11). Visitors see the welcome post in the Leggero theme.
To sign in, open FLATPRESS_LOGIN_URL (or choose Log in in the sidebar). Sign in as admin with FLATPRESS_ADMIN_PASSWORD.
The administration area
After you sign in, you land on the administration area. Its tabs are Entries (write and manage posts, categories, the spam word filter and the Comment Center), Statics (static pages), Uploader (images and files), Widgets, Plugins, Themes, Configuration (blog title, subtitle, footer, author, language and date format) and Maintain.
Write your first post
Open Entries > Write Entry and give the post a subject. Write the content with the BBCode toolbar (links, headings, lists, quotes, code, emoticons, files and images from the Uploader). Choose categories and select Publish. The post appears on the blog at once, stored as a plain file.
Use Configuration to set the blog title and subtitle (the image starts with "My FlatPress Blog"), the author name and the email address FlatPress shows. Change them to your own.
Static pages, comments, widgets and plugins
Statics lists the static pages. The Menu page is the sidebar menu, so edit it to add links. About and Privacy Policy are ordinary pages that you can rewrite or delete.
Readers can comment on posts. Anonymous commenters answer a small arithmetic question. The quick spam filter refuses comments that contain links (Entries > QuickSpamFilter, where you can change its word list). New comments wait in the Comment Center until you approve them.
Use Plugins to enable and disable FlatPress's standard plugins.
The notice about .htaccess on that page is expected. This image serves FlatPress through nginx, which does not use .htaccess files. The PrettyURLs plugin is set to its query string mode (addresses such as /?u=/2026/10/25/my-first-post/), which needs no rewrite rules.
Email: FlatPress sends comment notifications and contact form messages with PHP's mail(). The image has no mail server, so these messages are not delivered until you install and configure a mail transfer agent that relays through your mail provider.
Your blog data
Everything FlatPress stores lives in one folder, fp-content, which only the web server can read. The code around it belongs to root:
sudo ls -l /var/www/flatpress /var/www/flatpress/fp-content | sed -E 's/ +/ /g' | cut -d' ' -f1,3,4,9-/var/www/flatpress: total drwxr-xr-x root root admin -rw-r--r-- root root admin.php -rw-r--r-- root root blog.php -rw-r--r-- root root comments.php -rw-r--r-- root root contact.php -rw-r--r-- root root defaults.php drwxr-x--- www-data www-data fp-content drwxr-xr-x root root fp-defaults drwxr-xr-x root root fp-includes drwxr-xr-x root root fp-interface drwxr-xr-x root root fp-plugins -rw-r--r-- root root get.php -rw-r--r-- root root index.php -rw-r--r-- root root login.php -rw-r--r-- root root rss.php -rw-r--r-- root root search.php -rw-r--r-- root root sitemap.php -rw-r--r-- root root static.php /var/www/flatpress/fp-content: total -rw-r--r-- www-data www-data %%setup.lock drwxr-xr-x www-data www-data attachs drwxr-xr-x www-data www-data cache drwxr-xr-x www-data www-data compile drwxr-xr-x www-data www-data config drwxr-xr-x www-data www-data content drwxr-xr-x www-data www-data images drwxr-xr-x www-data www-data index drwxr-xr-x www-data www-data plugin_commentcenter drwxr-xr-x www-data www-data usersThe folders hold the following. config holds the settings and the hash salt. users holds the administrator record with its password hash. content holds the posts, comments and static pages. images and attachs hold your uploads. The login lockout keeps its counts in sac-login-guard.json.
Back up the blog by copying the folder (sudo tar -C /var/www/flatpress -czf flatpress-data.tar.gz fp-content) or with an EBS snapshot of the instance's volume.
To restore onto a new instance, let its first boot finish and stop nginx and PHP. Replace its fp-content with your copy (owned by www-data), then start nginx and PHP again.
Security checks
Port 80 sends browsers to HTTPS, and the health endpoint answers for load balancer checks. The setup wizard, the account and settings files, FlatPress's library code and a crafted page address are not reachable. Verify this with:
H=$(sed -n 's/^set \$fp_host "\(.*\)";$/\1/p' /etc/nginx/flatpress-host.conf)
curl -s -o /dev/null -w 'port 80: %{http_code} -> %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/login.php"
curl -sk --resolve "$H:443:127.0.0.1" "https://$H/healthz" for p in /setup.php /fp-content/users/admin.php /fp-content/config/settings.conf.php /fp-content/config/hashsalt.conf.php /fp-includes/core/core.users.php "/?page=../config/settings.conf"; do
curl -sk -o /dev/null -w "$p: %{http_code}\n" --resolve "$H:443:127.0.0.1" "https://$H$p" done
curl -s -o /dev/null -w 'unknown host name: %{http_code}\n' -H 'Host: example.com' http://127.0.0.1/ || trueport 80: 301 -> https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/login.php ok /setup.php: 404 /fp-content/users/admin.php: 404 /fp-content/config/settings.conf.php: 404 /fp-content/config/hashsalt.conf.php: 404 /fp-includes/core/core.users.php: 404 /?page=../config/settings.conf: 404 unknown host name: 000The value 000 means nginx closed the connection without an answer.
FlatPress has one account, and its password is stored only as a bcrypt hash. The blog id is your instance's own, and FlatPress builds its links from the configured HTTPS address. Confirm this with:
sudo -u www-data php /usr/local/lib/sac/flatpress/flatpress-admin.php summarywww=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ users=admin hashAlgo=bcrypt lock=yes theme=leggero bbcodeComments=on blogidDefault=noOnly root can read the credentials file, only the web server can read the blog data folder, and root owns the code:
sudo stat -c '%a %U:%G %n' /root/flatpress-credentials.txt /var/www/flatpress/fp-content /var/www/flatpress/index.php600 root:root /root/flatpress-credentials.txt 750 www-data:www-data /var/www/flatpress/fp-content 644 root:root /var/www/flatpress/index.phpThe certificate was generated on this instance and names its own addresses. Inspect it with:
H=$(sed -n 's/^set \$fp_host "\(.*\)";$/\1/p' /etc/nginx/flatpress-host.conf)
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltNamesubject=CN = ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhostnginx and PHP cannot reach the instance metadata service. PHP cannot run shell commands, is confined to FlatPress's folders and runs the login guard before every request. nginx hands PHP the configured host name and the real client address from the connection, never values that a client sends. Check these settings with:
systemctl show nginx -p IPAddressDeny
systemctl show php8.3-fpm -p IPAddressDeny php-fpm8.3 -i 2>/dev/null | grep -E '^(open_basedir|disable_functions|expose_php|auto_prepend_file) ' | cut -d' ' -f1-3 grep -E 'fastcgi_param (HTTP_HOST|SERVER_NAME|HTTP_X_FORWARDED_FOR|HTTP_CLIENT_IP) ' /etc/nginx/snippets/sac-flatpress-php.confIPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32 IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32 auto_prepend_file => /usr/local/lib/sac/flatpress/login-guard.php disable_functions => exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec expose_php => Off open_basedir => /var/www/flatpress/:/usr/local/lib/sac/flatpress/:/var/lib/php/sessions/:/tmp/ fastcgi_param HTTP_HOST $fp_host; fastcgi_param SERVER_NAME $fp_host; fastcgi_param HTTP_X_FORWARDED_FOR $remote_addr; fastcgi_param HTTP_CLIENT_IP $remote_addr;The upstream security fixes applied to 1.5.1 are listed with their FlatPress commit ids, and each changed file carries a dated notice. The full diff is /usr/share/doc/flatpress/sac-security-backport.diff. List the fixes with:
sed -n '1,9p' /usr/share/doc/flatpress/MODIFICATIONS grep -rlF --exclude-dir=fp-content 'sac: modified 2026-10-24' /var/www/flatpress | wc -lFlatPress 1.5.1 as shipped in this sac image is MODIFIED (GPL-2.0-only, section 2(a)). Date of change: 2026-10-24. Changed by: sac. What: the upstream FlatPress security fixes merged to master after the 1.5.1 release were backported verbatim: b6318487a15a3f3eaa886e98451c21df0081471e (PR #866) comment/contact form URL and name escaping (stored XSS) 1db538c549f51dda9e7c0fb594b8312a6f84d0ac (PR #936) static page id validation (drafts disclosed by path traversal) 350077232355369438f8813673b5c1cc6365c102 (PR #941, GHSA-26cp-8p3v-5f66) BBCode [video] attribute escaping ff5ab4ffe0473e14024d158cb9eadc749b8fb49d (PR #945, GHSA-q7fj-xg8x-4qxx) BBCode [color]/[size]/[align] escaping The combined diff is sac-security-backport.diff in this directory. Files changed: 11If you lock yourself out with ten wrong passwords, wait five minutes or clear the lockout with:
sudo flatpress-reset-lockoutYour own DNS name and certificate
Point a DNS record at the instance, then set the name that FlatPress is served as. This command creates a new self-signed certificate for the name and updates nginx and FlatPress's blog address (every link FlatPress builds). It also updates the URLs in /root/flatpress-credentials.txt and keeps the name across reboots:
sudo flatpress-set-hostname <your-domain>To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx. Running flatpress-set-hostname again replaces it with a self-signed certificate:
sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/flatpress.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/flatpress.key
sudo nginx -t && sudo systemctl reload nginxOnce browsers trust the certificate, you can also add an HSTS header (add_header Strict-Transport-Security "max-age=31536000" always;) to /etc/nginx/snippets/sac-flatpress-headers.conf. The image does not send one. Browsers ignore HSTS from a site whose certificate they do not trust, so it only takes effect with a trusted certificate.
Password and upgrades
flatpress-set-password gives the administrator a new random password, signs out every open session and records the password in /root/flatpress-credentials.txt. Run it without arguments, or pass your own password (24 to 64 letters and digits) on stdin. For example:
sudo flatpress-set-password printf '%s\n' '<your-new-password>' | sudo flatpress-set-password --stdinThe FlatPress code is read only to PHP, so you cannot add plugins and themes from the browser on this image. FlatPress installs them by copying folders into fp-plugins and fp-interface/themes, which you can do over SSH. For example, sudo cp -r <plugin> /var/www/flatpress/fp-plugins/ keeps them owned by root.
To upgrade an existing instance to a later FlatPress release, snapshot it, follow FlatPress's upgrade notes and copy the new code over everything except fp-content. Your blog is untouched, because it lives in fp-content.
Server components
These are the main components and where they live:
- FlatPress code (root owned): /var/www/flatpress
- Blog data (settings, account, posts, comments, uploads): /var/www/flatpress/fp-content
- nginx site: /etc/nginx/sites-available/sac-flatpress
- PHP settings: /etc/php/8.3/fpm/conf.d/99-sac-flatpress.ini
- Login guard: /usr/local/lib/sac/flatpress/login-guard.php
- Certificate and key: /etc/nginx/ssl/
- First boot script: /usr/local/sbin/flatpress-firstboot.sh
- Helpers: /usr/local/sbin/flatpress-set-hostname, /usr/local/sbin/flatpress-set-password, /usr/local/sbin/flatpress-reset-lockout
- Licence, release notes and the security diff: /usr/share/doc/flatpress/
- Credentials: /root/flatpress-credentials.txt (mode 0600)
Security recommendations
- Allow port 22 in the security group from your own networks only.
- Keep the administrator password secret, and rotate it if it may have been shared (Step 12).
- Use your own DNS name and a certificate that your browsers trust (Step 11).
- Keep the quick spam filter and comment moderation on unless you trust every commenter.
- Keep the operating system patched. Unattended security upgrades are on, and
sudo apt-get update && sudo apt-get upgradeapplies everything else. PHP-FPM and the PHP intl extension come from Ubuntu's universe repository.sudo pro security-status --esm-appsshows whether Ubuntu Pro (ESM Apps) has security updates for them (none were pending when the image was built). - Back up the
fp-contentfolder regularly (Step 9).
Support and licensing
FlatPress is open source under the GNU General Public License version 2 (a copy is at /usr/share/doc/flatpress/LICENSE.md). There is no per-user or per-CPU licence fee.
The FlatPress code in this image is modified as described in /usr/share/doc/flatpress/MODIFICATIONS. The upstream security fixes listed there are applied, and the setup wizard is removed. The AMI price covers SecureAnyCloud's hardening, first boot setup and support.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24 hour response SLA
Deploy on AWS
Launch FlatPress Blog and CMS 1.5.1 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com