Sites
Teddy Version: LatestCreating a new site and walkthrough of the site directory structure.
Definition
A site contains the content files for your static site. It is a collection of language-specific JSON-based site metadata files and language-specific markdown-based page content files. When your static site is built, each language-specific markdown-based page is converted into HTML and injected into a specified theme HTML template. Relevant language-specific site and page metadata is then also injected to generate the final HTML file representing the page in question. This stage of the build pipeline is illustrated in the following diagram.

Creating a new Site
The easiest way to create a new site, and to ensure that all the site prerequisites are met, is to simply make a copy of the demo TravelBook site directory found in $TEDDY_BASE/sites (ensure the copy is created in the same parent directory), and to then iteratively develop the copy based on your specific site content requirements. If you do not wish to use the demo TravelBook site as a template, then simply create a new directory in $TEDDY_BASE/sites. The name of the new directory, and hence the name of the new site, must only contain alphanumeric, hyphen and underscore characters. Lowercase site names are recommended, for example travelbook. For the purposes of this guide, we will hereafter refer to your site directory, for example $TEDDY_BASE/sites/travelbook, as $SITE_BASE.
Site Directory Structure
As a minimum, the following files and child directories must exist in $SITE_BASE.
$SITE_BASE/
├── languages/
│ └── {language}/
│ └── contributors.json
│ └── metadata.json
│ └── taxonomy.json <-- if site.collection.enabled is true
├── pages/...
├── site.json
Site Configuration
The site.json site configuration file must exist in the root of $SITE_BASE. For further information regarding the site configuration schema, and mandatory configuration namespaces, please read the site configuration guide.
Languages
As described in the site configuration guide, an array of language codes representing the languages that are supported by your site must be defined in site.languages.enabled. The first language specified in this array, for example 'en' (English), is considered the default language for your site. For every language specified in this array, a language directory named with the same language code must exist in $SITE_BASE/languages, for example $SITE_BASE/languages/en. Within each language directory, the following localised metadata files must exist as a minimum.
metadata.json
The $SITE_BASE/languages/{language}/metadata.json file contains the language-specific common metadata for your site, including the site title, description and comma-delimited keywords. For example, the English language version of the demo TravelBook site contains the following common metadata in $TEDDY_BASE/sites/travelbook/languages/en/metadata.json.
{
"metadata": {
"applicationName": "TravelBook",
"author": "Jillur Quddus",
"description": "Around the world with Teddy.",
"keywords": "africa, america, asia, australia, europe, holiday, flying, travel",
"title": "TravelBook"
}
}
contributors.json
The $SITE_BASE/languages/{language}/contributors.json file contains the language-specific metadata associated with the primary contributors to your site, including contributor ID, name and profile URL. Each contributor should be defined in their own JSON object with the schema authorId: { name: string, url: string, role: string, description: string, avatar: string }, where url, role, description and avatar (URL to an avatar image of the author) are all optional. For example, the English language version of the demo TravelBook site contains the following contributor metadata in $TEDDY_BASE/sites/travelbook/languages/en/contributors.json.
{
"contributors": {
"default": "teddy",
"teddy": {
"name": "Teddy",
"url": "https://demo.teddyful.com/about/"
}
}
}
taxonomy.json
If site.languages.enabled is set to true in the site configuration, and site.collection.taxonomy.categories is a non-empty array, then the $SITE_BASE/languages/{language}/taxonomy.json file must exist and must contain language-specific translations of the category keys defined in the site.collection.taxonomy.categories array. For example, the demo TravelBook site defines the following collection category keys in $TEDDY_BASE/sites/travelbook/site.json:
"collection": {
"enabled": true,
"pagesDir": "blog",
...
"taxonomy": {
"categories": [
"africa",
"asia",
"australia",
"europe",
"north-america",
"south-america"
]
},
...
}
Consequently, the English language version of the demo TravelBook site contains the following taxonomy metadata in $TEDDY_BASE/sites/travelbook/languages/en/taxonomy.json.
{
"taxonomy": {
"categories": {
"africa": "Africa",
"asia": "Asia",
"australia": "Australia",
"europe": "Europe",
"north-america": "North America",
"south-america": "South America"
}
}
}
For further information relating to language-specific metadata, please refer to the languages guide.
Pages
The $SITE_BASE/pages directory contains the language-specific content for each of the pages that comprise your site. Each page is a markdown-based flat-file that must conform to the following file naming convention: {template.name}.{language.code}.md, for example page.en.md.
Pages Directory Structure
The directory structure in $SITE_BASE/pages will reflect the page structure of your static site when it is built. Each unique page must exist in its own directory. For example, the demo TravelBook site has the following pages directory structure in $TEDDY_BASE/sites/travelbook/pages.
$SITE_BASE/
├── languages/
├── pages/
│ └── about/
│ └── about.en.md <-- English about page
│ └── about.ja.md <-- Japanese about page
│ └── about.zh-tw.md <-- Traditional Chinese about page
│ └── blog/
│ └── africa/
│ └── kenya/
| └── post.en.md <-- English blog post
| └── post.ja.md <-- Japanese blog post
| └── post.zh-tw.md <-- Traditional Chinese blog post
│ └── namibia/
| └── post.en.md
| └── post.ja.md
| └── post.zh-tw.md
│ └── tanzania/...
│ └── zambia/...
│ └── asia/
│ └── hong-kong/...
│ └── japan/...
│ └── malaysia/...
│ └── singapore/...
│ └── australia/
│ └── new-zealand/...
│ └── europe/
│ └── finland/...
│ └── norway/...
│ └── north-america/
│ └── canada/...
│ └── south-america/
│ └── bolivia/...
│ └── chile/...
│ └── peru/...
│ └── blog.en.md <-- English blog listing page
│ └── blog.ja.md <-- Japanese blog listing page
│ └── blog.zh-tw.md <-- Traditional Chinese blog listing page
│ └── page.en.md <-- English home page
│ └── page.ja.md <-- Japanese home page
│ └── page.zh-tw.md <-- Traditional Chinese home page
├── site.json
When the demo TravelBook site is built, the generated static site will have the following structure found in $TEDDY_BASE/sites/travelbook/public/{env}.
$SITE_BASE/public/{env}/
├── about/ <-- default language pages
│ └── index.html
├── blog/ <-- default language pages
│ └── africa/
│ └── kenya/index.html
│ └── namibia/index.html
│ └── tanzania/index.html
│ └── zambia/index.html
│ └── asia/
│ └── hong-kong/index.html
│ └── japan/index.html
│ └── malaysia/index.html
│ └── singapore/index.html
│ └── australia/
│ └── new-zealand/index.html
│ └── europe/
│ └── finland/index.html
│ └── norway/index.html
│ └── north-america/
│ └── canada/index.html
│ └── south-america/
│ └── bolivia/index.html
│ └── chile/index.html
│ └── peru/index.html
│ └── index.html
│ └── en/ <-- English language pages
│ └── about/index.html
│ └── blog/...
│ └── index.html <-- English language home page
│ └── ja/ <-- Japanese language pages
│ └── about/index.html
│ └── blog/...
│ └── index.html <-- Japanese language home page
│ └── zh-tw/ <-- Traditional Chinese language pages
│ └── about/index.html
│ └── blog/...
│ └── index.html <-- Traditional Chinese language home page
├── index.html <-- default language home page
The HTML pages for each language-specific subsite are created in $SITE_BASE/public/{env}/{language}. Furthermore, HTML pages corresponding to the default language are duplicated in the root of the generated static site. Continuing with the example of the demo TravelBook site, its various pages and resources can be accessed at the following URLs (assuming a base URL of https://demo.teddyful.com).
Home Page
- Default language (English): https://demo.teddyful.com
- Japanese 日本語: https://demo.teddyful.com/ja/
- Traditional Chinese 中文: https://demo.teddyful.com/zh-tw/
About Page
- Default language (English): https://demo.teddyful.com/about/
- Japanese 日本語: https://demo.teddyful.com/ja/about/
- Traditional Chinese 中文: https://demo.teddyful.com/zh-tw/about/
Blog Listing Page
- Default language (English): https://demo.teddyful.com/blog/
- Japanese 日本語: https://demo.teddyful.com/ja/blog/
- Traditional Chinese 中文: https://demo.teddyful.com/zh-tw/blog/
Blog Article (e.g. Zambia)
- Default language (English): https://demo.teddyful.com/blog/africa/zambia/
- Japanese 日本語: https://demo.teddyful.com/ja/blog/africa/zambia/
- Traditional Chinese 中文: https://demo.teddyful.com/zh-tw/blog/africa/zambia/
Page Template Name
As stated above, each page in $SITE_BASE/pages is a markdown-based flat-file that must conform to the following file naming convention: {template.name}.{language.code}.md, for example page.en.md. The template.name must correspond exactly to the name of a HTML template found in $TEDDY_BASE/themes/{theme.name}/templates, for example page.html. When your static site is built, each language-specific markdown-based page is converted into HTML and injected into the specified theme HTML template. For example, the content of the demo TravelBook blog article markdown page found at $TEDDY_BASE/sites/travelbook/pages/blog/africa/zambia/post.en.md will be converted into HTML and injected into the post.html template found in $TEDDY_BASE/themes/bear/templates/post.html. The final HTML page will be found in $TEDDY_BASE/sites/travelbook/public/{env}/en/blog/africa/zambia/index.html.
Minimum Pages
Note that at least one markdown-based page must exist in $SITE_BASE/pages, otherwise the build pipeline will fail and return a validation error when attempting to build your static site. For further information relating to pages, please read the pages guide.
Web Configuration
You may optionally provide site-specific web configuration files that will be deployed to the root of the public distribution directory when your static site is built.
robots.txt
If a robots.txt file is provided in $SITE_BASE/web/robots, then it will be deployed to the root of your static site when it is built. The demo TravelBook site provides an example robots.txt file in $TEDDY_BASE/sites/travelbook/web/robots/robots.txt as follows.
User-agent: *
Allow: /assets/images/favicon/
Allow: /assets/images/opengraph/
Disallow: /assets/
Sitemap: https://demo.teddyful.com/sitemap.xml
sitemap.xml
If a sitemap.xml file is provided in $SITE_BASE/web/sitemap, then it will be deployed to the root of your static site when it is built. The demo TravelBook site provides an example sitemap.xml file in $TEDDY_BASE/sites/travelbook/web/sitemap/sitemap.xml as follows.
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<!-- Priority 1.0 -->
<url>
<loc>https://demo.teddyful.com</loc>
<lastmod>2025-03-01T12:00:00+00:00</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://demo.teddyful.com/blog</loc>
<lastmod>2025-03-01T12:00:00+00:00</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<!-- Priority 0.8 -->
<url>
<loc>https://demo.teddyful.com/about</loc>
<lastmod>2025-03-01T12:00:00+00:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
</urlset>
Apache HTTP Server
If a .htaccess file is provided in $SITE_BASE/web/hosts/apache, and site.web.{env}.host is set to apache-http-server in the site configuration, then the .htaccess file will be deployed to the root of your static site when it is built. The demo TravelBook site provides an example .htaccess file in $TEDDY_BASE/sites/travelbook/web/hosts/apache/.htaccess as follows.
<IfModule mod_rewrite.c>
RewriteEngine On
# Remove .html extension
# If the file does not exist e.g. /app, then go to the file with the
# equivalent .html extension e.g. /app.html
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+)$ $1.html [NC,L]
## Begin - Exploits
# If you experience problems on your site block out the operations listed below
# This attempts to block the most common type of exploit attempts
#
# Block out any script trying to base64_encode data within the URL.
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
# Block out any script that includes a <script> tag in URL.
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
# Block out any script trying to set a PHP GLOBALS variable via URL.
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
# Block out any script trying to modify a _REQUEST variable via URL.
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
# Return 403 Forbidden header and show the content of the root homepage
RewriteRule .* index.html [F]
#
## End - Exploits
## Begin - Security
# Block all direct access to .md files:
RewriteRule \.md$ error [F]
# Block all direct access to files and folders beginning with a dot
RewriteRule (^|/)\.(?!well-known) - [F]
# Block access to specific files in the root folder
RewriteRule ^(LICENSE\.txt|\.htaccess)$ error [F]
## End - Security
</IfModule>
# Begin - Prevent Browsing and Set Default Resources
Options -Indexes
DirectoryIndex index.html
# End - Prevent Browsing and Set Default Resources
# Block requests containing malicious scripts
# Protect against clickjacking by blocking any frames requested
# from external locations
<IfModule mod_headers.c>
Header set X-XSS-Protection "1; mode=block"
Header set X-Frame-Options "SAMEORIGIN"
</IfModule>
# Redirect errors
ErrorDocument 404 https://demo.teddyful.com
Cloudflare Pages
If a _headers_ file is provided in $SITE_BASE/web/hosts/cloudflare/pages, and site.web.{env}.host is set to cloudflare-pages in the site configuration, then the _headers file will be deployed to the root of your static site when it is built. The demo TravelBook site provides an example _headers file in $TEDDY_BASE/sites/travelbook/web/hosts/cloudflare/pages/_headers as shown below. If you are deploying your site to Cloudflare Pages, you should review and update this _headers file according to your site requirements, in particular the cache headers.
/*
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer
X-XSS-Protection: 1; mode=block
/assets/*
X-Robots-Tag: nosnippet
Access-Control-Allow-Origin: *
/assets/collection/*
Cache-Control: public, max-age=86400, must-revalidate
/assets/css/style.min.css
Cache-Control: public, max-age=86400, must-revalidate
/assets/css/vendors/*
Cache-Control: public, max-age=2592000, must-revalidate
/assets/fonts/vendors/*
Cache-Control: public, max-age=2592000, must-revalidate
/assets/images/*
Cache-Control: public, max-age=604800, must-revalidate
/assets/js/blog.min.js
Cache-Control: public, max-age=86400, must-revalidate
/assets/js/site/*
Cache-Control: public, max-age=86400, must-revalidate
/assets/js/vendors/*
Cache-Control: public, max-age=2592000, must-revalidate
https://:project.pages.dev/*
X-Robots-Tag: noindex
Similarly, if a 404.html file is also provided in $SITE_BASE/web/hosts/cloudflare/pages, then it will also be copied to the root of your static site. Consequently, when a user attempts to request a non-existent URL in your Cloudflare Pages-hosted static site, the contents of this custom 404.html will be served by default. The demo TravelBook site provides an example 404.html file found at $TEDDY_BASE/sites/travelbook/web/hosts/cloudflare/pages/404.html that simply redirects users to the site's root index page.
Cloudflare Workers
If a _headers_ file is provided in $SITE_BASE/web/hosts/cloudflare/workers, and site.web.{env}.host is set to cloudflare-workers in the site configuration, then the _headers file will be deployed to the root of your static site when it is built. The demo TravelBook site provides an example _headers file in $TEDDY_BASE/sites/travelbook/web/hosts/cloudflare/workers/_headers as shown below. If you are deploying your site to Cloudflare Workers (Static Assets), you should review and update this _headers file according to your site requirements, in particular the cache headers.
/*
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer
X-XSS-Protection: 1; mode=block
/assets/*
X-Robots-Tag: nosnippet
Access-Control-Allow-Origin: *
/assets/collection/*
Cache-Control: public, max-age=86400, must-revalidate
/assets/css/style.min.css
Cache-Control: public, max-age=86400, must-revalidate
/assets/css/vendors/*
Cache-Control: public, max-age=2592000, must-revalidate
/assets/fonts/vendors/*
Cache-Control: public, max-age=2592000, must-revalidate
/assets/images/*
Cache-Control: public, max-age=604800, must-revalidate
/assets/js/blog.min.js
Cache-Control: public, max-age=86400, must-revalidate
/assets/js/site/*
Cache-Control: public, max-age=86400, must-revalidate
/assets/js/vendors/*
Cache-Control: public, max-age=2592000, must-revalidate
https://:version.:subdomain.workers.dev/*
X-Robots-Tag: noindex
Similarly, if a 404.html file is also provided in $SITE_BASE/web/hosts/cloudflare/workers, then it will also be copied to the root of your static site. Consequently, when a user attempts to request a non-existent URL in your Cloudflare Workers-hosted static site, the contents of this custom 404.html will be served by default. The demo TravelBook site provides an example 404.html file found at $TEDDY_BASE/sites/travelbook/web/hosts/cloudflare/workers/404.html that simply redirects users to the site's root index page.
Static Assets
You may optionally provide static CSS, JavaScript, font and image assets that will be copied to $SITE_BASE/public/{env}/assets when your static site is built. Site-specific static assets should be placed in $SITE_BASE/assets and conform to the following directory structure.
$SITE_BASE/
├── assets/
│ └── css/
│ └── fonts/
│ └── images/
│ └── js/
│ └── videos/
Should you wish to minify any custom site-specific CSS and/or JavaScript assets during the build, then you should list the relative paths to those asset files in the site.assets.custom.css (relative to $SITE_BASE/assets/css) and site.assets.custom.js (relative to $SITE_BASE/assets/js) site configuration arrays respectively. Please read the site configuration guide for further details.
