Headless PHP Frontend: Installation and Usage Manual
Build a Fast Headless Website With PHP, HTML, CSS, JavaScript and WordPress In the previous manual, we installed the Full Stack App WordPress plugin and turned a fresh WordPress installation into a structured headless backend. Now we will use the other half of the project: the PHP frontend package. This package provides a lightweight public-facing […]
Build a Fast Headless Website With PHP, HTML, CSS, JavaScript and WordPress
In the previous manual, we installed the Full Stack App WordPress plugin and turned a fresh WordPress installation into a structured headless backend.
Now we will use the other half of the project:
the PHP frontend package.
This package provides a lightweight public-facing website that can sit in front of the WordPress backend.
The basic architecture is:
WordPress
Headless Backend
β
β API / Data
βΌ
βββββββββββββββββββββββ
β PHP Frontend β
β β
β PHP β
β HTML β
β CSS β
β JavaScript β
ββββββββββββ¬βββββββββββ
β
βΌ
Browser
The purpose of this package is not to give you another WordPress theme.
It gives you a separate PHP application layer that can be customized and used as the public website.
1. What This Package Is
The frontend package is a lightweight PHP website.
It provides the basic building blocks for:
- a homepage
- article listings
- individual articles
- application listings
- application detail pages
- CMS pages
- navigation
- pagination
- responsive design
- dark mode
- application downloads
- basic SEO metadata
- shared header and footer
The technology stack is intentionally small:
PHP
HTML
CSS
JavaScript
WordPress
There is no requirement for React, Vue, Angular, Next.js or another large frontend framework.
The browser ultimately receives ordinary HTML.
2. What You Need
For local development, we recommend:
Docker
DDEV
A working WordPress backend
PHP
A web browser
A terminal
If you followed the previous backend manual, you already have most of what you need.
The backend should already provide:
WordPress
+
Full Stack App Plugin
+
REST API
The frontend will sit alongside it.
3. The Two-Part Project
A complete headless installation can be organized like this:
my-headless-project/
β
βββ backend/
β βββ WordPress
β βββ Full Stack App
β
βββ frontend/
βββ index.php
βββ article.php
βββ application.php
βββ page.php
βββ config.php
βββ includes/
βββ assets/
The backend and frontend can eventually live on separate domains or subdomains.
For example:
cms.example.com
β
WordPress backend
www.example.com
β
PHP frontend
During development, however, keeping everything local is considerably easier.
4. Download the Frontend Package
The reusable frontend package is distributed as:
frontend.zip
Extract the archive.
You should see the frontend application files.
The important structure is:
frontend/
β
βββ index.php
βββ article.php
βββ application.php
βββ page.php
βββ config.php
β
βββ includes/
β βββ header.php
β βββ footer.php
β
βββ assets/
βββ style.css
βββ script.js
This is the application we are going to customize.
5. Where Should the Frontend Live?
There are several possible deployment models.
Model A: Same Server
Server
β
βββ WordPress
β
βββ PHP Frontend
This is the simplest deployment model.
Model B: Separate Subdomains
cms.example.com
β
βΌ
WordPress
www.example.com
β
βΌ
PHP Frontend
This provides stronger physical separation.
Model C: Separate Servers
Server A
WordPress
β
β HTTPS / JSON
βΌ
Server B
PHP Frontend
This is also possible because the frontend can communicate with the backend over HTTP.
For learning and local development, we will start with the simplest arrangement.
6. Important Architecture Note
The frontend package is designed around the headless architecture we developed.
The backend provides:
/wp-json/fullstack/v1/
and the frontend consumes the backend’s data.
The current reference implementation also has the ability to bootstrap WordPress directly through wp-load.php.
That gives us a useful hybrid development model:
PHP Frontend
β
βββ WordPress bootstrap
β
βββ API configuration
β
βΌ
WordPress
The longer-term architecture can be completely separated:
PHP Frontend
β
β HTTPS / JSON
βΌ
WordPress REST API
The important point is that the frontend is not dependent on a WordPress theme.
It owns the public presentation.
7. Configure the Frontend
The first file you should inspect is:
config.php
This is where environment-specific configuration belongs.
For example, the API URL can be configured as:
define(
'WP_API_URL',
'https://example.com/wp-json/fullstack/v1/apps'
);
Change the URL so that it points to your own WordPress installation.
For example, during local development:
define(
'WP_API_URL',
'https://my-headless-site.ddev.site/wp-json/fullstack/v1/apps'
);
Do not leave the example production URL in place.
8. Why Configuration Matters
The frontend should not contain your backend URL in dozens of different files.
Instead, configuration should be centralized.
Conceptually:
config.php
β
βΌ
Backend configuration
β
βββββββββββ΄ββββββββββ
βΌ βΌ
Frontend API calls
If you later move the WordPress backend from:
cms.example.com
to:
you should be able to change the relevant configuration rather than search through the entire frontend codebase.
9. Configure the Local WordPress Path
The reference frontend also uses WordPress directly in several entry points.
You may therefore see code similar to:
define('WP_USE_THEMES', false);
require_once __DIR__ . '/dashboard/wp-load.php';
This assumes that WordPress exists at:
dashboard/
relative to the frontend application.
If your WordPress installation is somewhere else, change the path accordingly.
For example:
require_once __DIR__ . '/../backend/wordpress/wp-load.php';
The exact path depends on your project layout.
The important thing is that PHP must be able to locate WordPress’s:
wp-load.php
10. A Simple Local Project Layout
One convenient development layout is:
my-headless-project/
β
βββ backend/
β βββ wordpress/
β βββ wp-admin/
β βββ wp-content/
β βββ wp-includes/
β βββ wp-load.php
β
βββ frontend/
βββ index.php
βββ article.php
βββ application.php
βββ page.php
βββ config.php
βββ includes/
βββ assets/
In this arrangement, the frontend can bootstrap WordPress using:
require_once __DIR__ . '/../backend/wordpress/wp-load.php';
This is convenient for local development because both halves of the application remain inside one project.
11. Running the Frontend With DDEV
If you want DDEV to serve the frontend directly, create a DDEV project around the frontend directory.
For example:
cd frontend
ddev config
Then choose an appropriate PHP project configuration.
Start it:
ddev start
The exact DDEV configuration can vary depending on whether the frontend is running independently or as part of the same project as WordPress.
The key requirement is simple:
The PHP frontend must be executed by a PHP-capable web server.
12. The Frontend Entry Point
The main entry point is:
index.php
This is the homepage.
It determines which collection should be displayed.
The current implementation supports:
Applications
Articles
The selected tab is read from the query string:
$tab = $_GET['tab'] ?? 'apps';
So:
/?tab=apps
displays applications.
And:
/?tab=posts
displays articles.
13. The Homepage Application Listing
When Applications are selected, the frontend queries:
app_repository
The basic query is:
$query_args = [
'post_type' => 'app_repository',
'post_status' => 'publish',
'posts_per_page' => $per_page,
'paged' => $paged,
];
This is the custom post type created by the Full Stack App backend.
The frontend then turns each WordPress application into an application card.
14. Application Cards
Each application card can display:
Application title
Version
Operating system
Description
Updated date
Read
Download
The version comes from application metadata:
$version = get_post_meta(
get_the_ID(),
'app_version',
true
) ?: '1.0.0';
The operating system is retrieved similarly:
$os = get_post_meta(
get_the_ID(),
'app_os',
true
) ?: 'Linux / Ubuntu';
The downloadable file is resolved from its attachment ID.
The frontend can therefore display a complete application card without maintaining a separate application database.
15. The Articles Tab
Switching to:
/?tab=posts
causes the frontend to query ordinary WordPress posts.
The query changes to:
'post_type' => 'post'
The page can then display:
Article title
Excerpt
Publication date
Read Article
The same pagination system can be used for both applications and articles.
16. Pagination
The frontend displays six items per page:
$per_page = 6;
The current page is determined from:
$paged = isset($_GET['page'])
? max(1, intval($_GET['page']))
: 1;
If more pages exist, the frontend generates pagination links.
For example:
1 2 3 4
A second page of applications may therefore be:
/?tab=apps&page=2
while the second page of articles is:
/?tab=posts&page=2
The selected tab is preserved.
17. Clean Article URLs
The frontend uses clean public URLs.
An article can be accessed through:
/articles/my-first-article
Internally, the request is routed to:
article.php
with the article slug made available to the PHP application.
The visitor does not need to know how the PHP files are organized.
That is the difference between an implementation path and a public URL.
18. Article Pages
The article entry point is:
article.php
It retrieves the requested slug:
$slug = isset($_GET['slug'])
? sanitize_title(wp_unslash($_GET['slug']))
: '';
It then queries the published post.
If the article exists, the frontend renders:
Title
Metadata
Featured image
Content
If the article does not exist, the frontend returns a proper 404 response.
19. Application Detail Pages
Applications use the same pattern.
The public URL becomes:
/applications/my-application
The frontend retrieves the slug and queries the:
app_repository
post type.
The resulting page can display:
Application title
Version
Operating system
Description
Application content
Download button
This gives the application repository a proper public-facing detail page.
20. CMS Pages
The frontend also supports ordinary WordPress pages.
Examples include:
/about
/contact
/privacy-policy
/terms
The page.php entry point handles these pages.
The current frontend explicitly restricts the allowed page slugs.
This is useful because the frontend should not blindly treat every possible URL as a valid public page.
21. Shared Header
The common site header is located at:
includes/header.php
Instead of repeating the HTML document structure in every PHP file, each page can include the shared header.
The header is responsible for things such as:
DOCTYPE
HTML head
Title
Meta description
Canonical URL
Stylesheet
Navigation
Theme toggle
This means a change to the global navigation can be made in one place.
22. Shared Footer
The footer lives at:
includes/footer.php
It provides the common closing structure:
Footer
Copyright
Legal links
JavaScript
Closing HTML
Again, the goal is to keep repeated markup out of individual pages.
23. Page Metadata
The frontend generates metadata server-side.
For an article, the title can become:
<title>Article Title</title>
The description is derived from the article content or excerpt.
The canonical URL becomes something similar to:
/articles/my-first-article
This gives the frontend control over the public HTML document and basic SEO structure.
24. Styling the Frontend
The main stylesheet is:
assets/style.css
It contains the visual system for the application.
The stylesheet defines CSS custom properties such as:
:root {
--bg-color: #f8fafc;
--text-main: #0f172a;
--text-muted: #64748b;
--card-bg: #ffffff;
--primary: #2563eb;
--border: #e2e8f0;
}
These variables make customization easier.
For example, changing:
--primary
changes the main accent color throughout the application.
25. Customizing the Brand
The frontend package is intended to be customized.
You can change:
Site name
Logo
Navigation
Colors
Typography
Card design
Footer
Buttons
Spacing
Most visual customization can be done in:
assets/style.css
while site-wide HTML changes can be made in:
includes/header.php
includes/footer.php
This gives a developer a clear place to start.
26. Responsive Layout
The frontend uses CSS Grid and media queries rather than a large CSS framework.
The application grid can use:
.app-grid {
display: grid;
gap: 24px;
grid-template-columns:
repeat(auto-fill, minmax(320px, 1fr));
}
On smaller screens, the grid can collapse to one column.
This gives us:
Desktop
ββββββββββ ββββββββββ ββββββββββ
β App β β App β β App β
ββββββββββ ββββββββββ ββββββββββ
Mobile
βββββββββββββββββββ
β App β
βββββββββββββββββββ
βββββββββββββββββββ
β App β
βββββββββββββββββββ
No JavaScript layout library is required.
27. Dark Mode
The frontend includes a small JavaScript enhancement for dark mode.
The browser stores the user’s preference in:
localStorage
The JavaScript applies:
dark-theme
to the page.
The CSS then changes the variables.
For example:
body.dark-theme {
--bg-color: #0f172a;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--card-bg: #1e293b;
--border: #334155;
}
The important design principle is that JavaScript remembers the choice while CSS controls the appearance.
28. JavaScript Enhancements
The main JavaScript file is:
assets/script.js
The JavaScript layer is intentionally small.
It handles things such as:
Theme preference
Theme switching
Download notification
Small interface enhancements
The primary content does not depend on JavaScript.
That means an article can still be rendered as HTML even if JavaScript is unavailable.
This is an example of progressive enhancement.
29. Download Notifications
When an application download is triggered, JavaScript can display a small notification:
Downloading My Application...
The actual download does not depend on JavaScript.
The browser follows the download URL normally.
JavaScript simply improves the user experience.
This distinction is important.
The frontend should not turn simple browser functionality into a complicated JavaScript subsystem.
30. Security and Escaping
When customizing the frontend, continue using the appropriate WordPress escaping functions.
For ordinary text:
esc_html()
For URLs:
esc_url()
For HTML attributes:
esc_attr()
When reading query-string values, sanitize them appropriately.
For example:
sanitize_title(
wp_unslash($_GET['slug'])
)
Do not remove these protections simply because the frontend is a custom PHP application.
The frontend is still receiving and displaying data.
31. Changing the Site Name
The site name is normally defined in the shared presentation layer.
Open:
includes/header.php
and locate the site branding.
Replace the existing name with your own.
For example:
My Application
You can also replace the navigation items.
This is one of the first customizations you should make when creating a new site from the package.
32. Changing Navigation
The navigation belongs in:
includes/header.php
You can add links such as:
Home
Articles
Applications
About
Contact
If your application needs different sections, change the navigation accordingly.
The navigation is presentation-layer code.
The WordPress backend does not need to know how you choose to arrange those links.
33. Creating a New Page Type
Suppose you want to add a new public page type.
A simple approach is to create another PHP entry point:
documentation.php
Then add the appropriate routing rule.
The page can reuse:
includes/header.php
and:
includes/footer.php
This is one of the advantages of the simple PHP architecture.
You don’t need to understand an enormous framework before adding a page.
You can follow the existing pattern.
34. Creating New Components
If a piece of markup is repeated frequently, extract it.
For example:
includes/
header.php
footer.php
application-card.php
article-card.php
Then individual pages can include those components.
As the application grows, this can evolve into a more formal component system.
But we don’t need to build that abstraction until repetition actually justifies it.
35. Changing the Number of Items Per Page
The current homepage uses:
$per_page = 6;
If you want eight items:
$per_page = 8;
If you want twelve:
$per_page = 12;
The same pagination mechanism will continue to work.
36. Connecting to a Remote WordPress Backend
One of the most interesting uses of the package is separating the frontend and backend completely.
For example:
Frontend
https://www.example.com
β
β HTTPS
βΌ
Backend
https://cms.example.com
The frontend can be configured to communicate with the remote backend API.
The backend URL should use HTTPS:
https://cms.example.com/wp-json/fullstack/v1/
The frontend then becomes independent of the physical location of the WordPress installation.
37. CORS When Frontend and Backend Are Separate
When the frontend and backend run on different origins, the browser enforces CORS rules.
The backend therefore needs to allow the frontend origin.
For example:
Frontend:
https://www.example.com
Backend:
https://cms.example.com
The backend must be configured to allow requests from:
https://www.example.com
Do not use a wildcard origin in production unless you have a specific reason to do so.
The allowed origins should match your actual deployment architecture.
38. Running the Frontend Without WordPress on the Same Host
A completely separated deployment can look like:
SERVER 1
WordPress
Full Stack App
REST API
β
β HTTPS
βΌ
SERVER 2
PHP Frontend
This is one of the strongest arguments for building the application around an API.
The two systems can evolve independently.
You can replace the frontend without replacing the CMS.
You can improve the backend without redesigning the public website.
39. Testing the Frontend
Before considering the frontend ready, test the following:
Homepage
Articles tab
Applications tab
Pagination
Article detail
Application detail
CMS pages
404 pages
Navigation
Mobile layout
Dark mode
Download buttons
Also test:
No articles
No applications
Missing article
Missing application
Long article title
Long application description
Missing featured image
Missing download file
A reusable frontend should behave sensibly when content is incomplete.
40. Common Problems
The frontend shows a WordPress error
Check that the wp-load.php path is correct.
For example:
require_once __DIR__ . '/../backend/wordpress/wp-load.php';
The path must match your actual project structure.
The API URL is wrong
Check:
config.php
and verify that the backend URL points to the WordPress installation running the Full Stack App plugin.
Applications are missing
Check:
- The Full Stack App plugin is active.
- The
app_repositorycontent exists. - The applications are published.
- The application metadata is present.
- The frontend is pointing to the correct backend.
Articles are missing
Check:
- WordPress posts exist.
- They are published.
- The frontend is querying the correct WordPress installation.
- The WordPress bootstrap path is correct.
Images are missing
Check:
- the WordPress media attachment exists
- the image URL is valid
- the browser can access the backend domain
- HTTPS is configured correctly
- CORS or mixed-content restrictions are not blocking the resource
41. Recommended Development Workflow
Once the project is running, a good development cycle is:
1. Edit PHP
β
2. Refresh browser
β
3. Inspect HTML
β
4. Check WordPress/API data
β
5. Edit CSS/JavaScript
β
6. Test desktop
β
7. Test mobile
When debugging content problems, test the backend independently.
For example:
/wp-json/fullstack/v1/posts
If the API is wrong, fix the backend.
If the API is correct but the page is wrong, fix the frontend.
This separation can save a surprising amount of debugging time.
42. Deployment
Once development is complete, the frontend can be deployed to a PHP-capable server.
The basic deployment process is:
Upload frontend files
β
Configure PHP
β
Configure WordPress path/API
β
Configure domain
β
Enable HTTPS
β
Test public URLs
If the frontend and backend are on separate servers, make sure the backend API is publicly reachable from the frontend’s environment and that the appropriate CORS configuration is in place.
43. What Should Be Customized?
When using this package for a new project, I recommend changing the following first:
Identity
Site name
Logo
Favicon
Navigation
Menu items
URLs
Visual design
Colors
Typography
Spacing
Cards
Buttons
Content types
Articles
Applications
Pages
URLs
/articles/...
/applications/...
Metadata
Title
Description
Canonical URLs
The core rendering pattern can remain intact.
44. The Frontend as a Reusable Starting Point
The important idea behind this package is not that every website must look exactly like the reference implementation.
It should not.
The package is intended to provide a starting point.
Think of it as:
Frontend Foundation
β
βββ Layout
βββ Routing pattern
βββ Templates
βββ API integration
βββ Responsive CSS
βββ JavaScript foundation
β
βΌ
Your Application
You customize the foundation for the project you are building.
45. The Complete Headless System
Once both packages are installed, the architecture becomes:
VISITOR
β
βΌ
βββββββββββββββ
β Browser β
ββββββββ¬βββββββ
β
βΌ
βββββββββββββββ
β PHP Frontendβ
β β
β PHP β
β HTML β
β CSS β
β JavaScript β
ββββββββ¬βββββββ
β
β API
βΌ
βββββββββββββββ
β WordPress β
β Backend β
β β
β Full Stack β
β App Plugin β
ββββββββ¬βββββββ
β
βΌ
WordPress DB
The two packages have different responsibilities.
The backend manages the information.
The frontend manages the experience.
46. Final Installation Checklist
A new website built from this frontend should pass the following checklist:
- WordPress backend is installed
- Full Stack App plugin is active
/wp-json/fullstack/v1/statusworks- Posts API works
- Categories API works
- Applications API works
- Frontend package is extracted
config.phpis configured- WordPress path is correct if direct bootstrapping is used
- Frontend opens successfully
- Homepage works
- Articles work
- Applications work
- CMS pages work
- Pagination works
- 404 handling works
- Images load
- Downloads work
- Dark mode works
- Mobile layout works
- HTTPS works
- Production API/CORS configuration has been reviewed
47. Final Result
We started with two separate technologies:
WordPress
+
PHP
and gave them separate responsibilities.
WordPress became the content and application backend.
The Full Stack App plugin became the API layer.
The PHP package became the public-facing application.
CSS became the visual system.
JavaScript became a lightweight enhancement layer.
The final result is a headless website architecture that looks like this:
βββββββββββββββββββββ
β WordPress β
β β
β CMS + Database β
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β Full Stack App β
β Plugin β
β β
β REST API β
βββββββββββ¬ββββββββββ
β
β JSON / Data
βΌ
βββββββββββββββββββββ
β PHP Frontend β
β β
β PHP + HTML β
β CSS + JavaScript β
βββββββββββ¬ββββββββββ
β
βΌ
Browser
And that is the real purpose of the package.
It is not simply a collection of PHP files.
It is a reusable starting point for building a headless website with WordPress as the backend and PHP as the public-facing application layer.
The reference website, sanjibsinha.in, is one implementation of that architecture.
The backend and frontend packages allow us to take the same underlying ideas and build something entirely different on top of them.
That is where this project becomes particularly interesting.
We have moved from building one website to creating the foundation for building the next website.