diff --git a/src/nginxconfig/generators/proxy.conf.js b/src/nginxconfig/generators/proxy.conf.js
new file mode 100644
index 0000000..bff4e65
--- /dev/null
+++ b/src/nginxconfig/generators/proxy.conf.js
@@ -0,0 +1,18 @@
+export default () => {
+    const config = {};
+
+    config.proxy_http_version = '1.1';
+    config.proxy_cache_bypass = '$http_upgrade';
+
+    config['proxy_set_header Upgrade'] = '$http_upgrade';
+    config['proxy_set_header Connection'] = '"upgrade"';
+    config['proxy_set_header Host'] = '$host';
+    config['proxy_set_header X-Real-IP'] = '$remote_addr';
+    config['proxy_set_header X-Forwarded-For'] = '$proxy_add_x_forwarded_for';
+    config['proxy_set_header X-Forwarded-Proto'] = '$scheme';
+    config['proxy_set_header X-Forwarded-Host'] = '$host';
+    config['proxy_set_header X-Forwarded-Port'] = '$server_port';
+
+    // Done!
+    return config;
+};
diff --git a/src/nginxconfig/generators/python_uwsgi.conf.js b/src/nginxconfig/generators/python_uwsgi.conf.js
new file mode 100644
index 0000000..f042ead
--- /dev/null
+++ b/src/nginxconfig/generators/python_uwsgi.conf.js
@@ -0,0 +1,17 @@
+export default (domains, global) => {
+    const config = {};
+
+    config['# default uwsgi_params'] = '';
+    config.include = 'uwsgi_params';
+
+    config['# uwsgi settings'] = '';
+    config.uwsgi_pass = (global.python.pythonServer.computed[0] === '/' ? 'unix:' : '')
+        + global.python.pythonServer.computed;
+    config['uwsgi_param Host'] = '$host';
+    config['uwsgi_param X-Real-IP'] = '$remote_addr';
+    config['uwsgi_param X-Forwarded-For'] = '$proxy_add_x_forwarded_for';
+    config['uwsgi_param X-Forwarded-Proto'] = '$http_x_forwarded_proto';
+
+    // Done!
+    return config;
+};
diff --git a/src/nginxconfig/generators/security.conf.js b/src/nginxconfig/generators/security.conf.js
new file mode 100644
index 0000000..90e1029
--- /dev/null
+++ b/src/nginxconfig/generators/security.conf.js
@@ -0,0 +1,29 @@
+import commonHsts from '../util/common_hsts';
+
+export default (domains, global) => {
+    const config = {};
+
+    config['# security headers'] = '';
+    config['add_header X-Frame-Options'] = '"SAMEORIGIN" always';
+    config['add_header X-XSS-Protection'] = '"1; mode=block" always';
+    config['add_header X-Content-Type-Options'] = '"nosniff" always';
+    config['add_header Referrer-Policy'] = `"${global.security.referrerPolicy.computed}" always`;
+
+    if (global.security.contentSecurityPolicy.computed)
+        config['add_header Content-Security-Policy'] = `"${global.security.contentSecurityPolicy.computed}" always`;
+
+    // Every domain has HSTS enabled, and they all have same hstsSubdomains/hstsPreload settings
+    if (commonHsts(domains)) {
+        const commonHSTSSubdomains = domains.length && domains[0].https.hstsSubdomains.computed;
+        const commonHSTSPreload = domains.length && domains[0].https.hstsPreload.computed;
+        config['add_header Strict-Transport-Security'] = `"max-age=31536000${commonHSTSSubdomains ? '; includeSubDomains' : ''}${commonHSTSPreload ? '; preload' : ''}" always`;
+    }
+
+    config['# . files'] = '';
+    config['location ~ /\\.(?!well-known)'] = {
+        deny: 'all',
+    };
+
+    // Done!
+    return config;
+};
diff --git a/src/nginxconfig/generators/website.conf.js b/src/nginxconfig/generators/website.conf.js
new file mode 100644
index 0000000..dac3aa9
--- /dev/null
+++ b/src/nginxconfig/generators/website.conf.js
@@ -0,0 +1,86 @@
+import { getSslCertificate, getSslCertificateKey } from '../util/get_ssl_certificate';
+import commonHsts from '../util/common_hsts';
+import securityConf from './security.conf';
+
+export default (domain, domains, global) => {
+    // Use kv so we can use the same key multiple times
+    const config = [];
+
+    // Build the server config on its own before adding it to the parent config
+    const serverConfig = [];
+    const ipv4Pre = domain.server.listenIpv4.computed === '*' ? '' : `${domain.server.listenIpv4.computed}:`;
+
+    // Not HTTPS or not force HTTPS
+    if (!domain.https.https.computed || !domain.https.forceHttps.computed) {
+        serverConfig.push(['listen', `${ipv4Pre}80`]);
+
+        // v6
+        if (domain.server.listenIpv6.computed)
+            serverConfig.push(['listen', `[${domain.server.listenIpv6.computed}]:80`]);
+    }
+
+    // HTTPS
+    if (domain.https.https.computed) {
+        serverConfig.push(['listen', `${ipv4Pre}443 ssl${domain.https.http2.computed ? ' http2' : ''}`]);
+
+        // v6
+        if (domain.server.listenIpv6.computed)
+            serverConfig.push(['listen',
+                `[${domain.server.listenIpv6.computed}]:443 ssl${domain.https.http2.computed ? ' http2' : ''}`]);
+    }
+
+    serverConfig.push(['server_name',
+        `${domain.server.wwwSubdomain.computed ? 'www.' : ''}${domain.server.domain.computed}`]);
+
+    // PHP or Django
+    if (domain.php.php.computed || (domain.python.python.computed && domain.python.djangoRules.computed)) {
+        serverConfig.push(['set', `$base ${domain.server.path.computed}`]);
+
+        // root
+        if (domain.routing.root.computed)
+            serverConfig.push(['root', `$base${domain.server.documentRoot.computed}`]);
+    }
+
+    // Not PHP and not Django and root
+    if (!domain.php.php.computed
+        && (!domain.python.python.computed || !domain.python.djangoRules.computed)
+        && domain.routing.root.computed)
+        serverConfig.push(['root', `${domain.server.path.computed}${domain.server.documentRoot.computed}`]);
+
+    // HTTPS
+    if (domain.https.https.computed) {
+        serverConfig.push(['# SSL', '']);
+        serverConfig.push(['ssl_certificate', getSslCertificate(domain, global)]);
+        serverConfig.push(['ssl_certificate_key', getSslCertificateKey(domain, global)]);
+
+        // Let's encrypt
+        if (domain.https.certType.computed === 'letsEncrypt')
+            serverConfig.push(['ssl_trusted_certificate',
+                `/etc/letsencrypt/live/${domain.server.domain.computed}/chain.pem`]);
+    }
+
+    // HSTS
+    if (!commonHsts(domains) && domain.https.hsts.computed) {
+        serverConfig.push(['# HSTS', '']);
+        serverConfig.push(['add_header Strict-Transport-Security',
+            `'"max-age=31536000${domain.https.hstsSubdomains.computed ? '; includeSubDomains' : ''}${domain.https.hstsPreload.computed ? '; preload' : ''}" always'`]);
+    }
+
+    // Security
+    if (global.tools.modularizedStructure.computed) {
+        // Modularized
+        serverConfig.push(['# security', '']);
+        serverConfig.push(['include', 'nginxconfig.io/security.conf']);
+    } else {
+        // Unified
+        serverConfig.push(...Object.entries(securityConf(domains, global)));
+    }
+
+    // Access log or error log for domain
+    // TODO: this & beyond
+
+    // Add the server config to the parent config now its built
+    config.push(['server', serverConfig]);
+
+    return config;
+};
diff --git a/src/nginxconfig/generators/wordpress.conf.js b/src/nginxconfig/generators/wordpress.conf.js
new file mode 100644
index 0000000..1ad0aac
--- /dev/null
+++ b/src/nginxconfig/generators/wordpress.conf.js
@@ -0,0 +1,44 @@
+export default (domains, global) => {
+    const config = {};
+
+    config['# WordPress: allow TinyMCE'] = '';
+    config['location = /wp-includes/js/tinymce/wp-tinymce.php'] = {
+        include: 'nginxconfig.io/php_fastcgi.conf',
+    };
+
+    config['# WordPress: deny wp-content, wp-includes php files'] = '';
+    config['~* ^/(?:wp-content|wp-includes)/.*\\.php$'] = {
+        deny: 'all',
+    };
+
+    config['# WordPress: deny wp-content/uploads nasty stuff'] = '';
+    config['location ~* ^/wp-content/uploads/.*\\.(?:s?html?|php|js|swf)$'] = {
+        deny: 'all',
+    };
+
+    config['# WordPress: deny wp-content/plugins (except earlier rules)'] = '';
+    config['location ~ ^/wp-content/plugins'] = {
+        deny: 'all',
+    };
+
+    config['# WordPress: deny scripts and styles concat'] = '';
+    config['location ~* \\/wp-admin\\/load-(?:scripts|styles)\\.php'] = {
+        deny: 'all',
+    };
+
+    config['# WordPress: deny general stuff'] = '';
+    config['location ~* ^/(?:xmlrpc\\.php|wp-links-opml\\.php|wp-config\\.php|wp-config-sample\\.php|wp-comments-post\\.php|readme\\.html|license\\.txt)$'] = {
+        deny: 'all',
+    };
+
+    if (global.security.limitReq.computed) {
+        config['# WordPress: throttle wp-login.php'] = '';
+        config['location = /wp-login.php'] = {
+            limit_req: 'zone=login burst=2 nodelay',
+            include: 'nginxconfig.io/php_fastcgi.conf',
+        };
+    }
+
+    // Done!
+    return config;
+};
diff --git a/src/nginxconfig/util/common_hsts.js b/src/nginxconfig/util/common_hsts.js
new file mode 100644
index 0000000..c828a6f
--- /dev/null
+++ b/src/nginxconfig/util/common_hsts.js
@@ -0,0 +1,11 @@
+export default domains => {
+    return domains.every(d => d.https.hsts.computed)
+        && (
+            domains.every(d => d.https.hstsSubdomains.computed)
+            || domains.every(d => !d.https.hstsSubdomains.computed)
+        )
+        && (
+            domains.every(d => d.https.hstsPreload.computed)
+            || domains.every(d => !d.https.hstsPreload.computed)
+        );
+};
diff --git a/src/nginxconfig/util/get_ssl_certificate.js b/src/nginxconfig/util/get_ssl_certificate.js
new file mode 100644
index 0000000..6be1251
--- /dev/null
+++ b/src/nginxconfig/util/get_ssl_certificate.js
@@ -0,0 +1,19 @@
+export const getSslCertificate = (domain, global) => {
+    if (domain.https.certType.computed === 'letsEncrypt')
+        `/etc/letsencrypt/live/${domain.server.domain.computed}/fullchain.pem`;
+
+    if (domain.https.sslCertificate.computed)
+        return domain.https.sslCertificate.computed;
+
+    return `${global.nginx.nginxConfigDirectory.computed.replace(/\/+$/, '')}/ssl/${domain.server.domain.computed}.crt`;
+};
+
+export const getSslCertificateKey = (domain, global) => {
+    if (domain.https.certType.computed === 'letsEncrypt')
+        `/etc/letsencrypt/live/${domain.server.domain.computed}/privkey.pem`;
+
+    if (domain.https.sslCertificateKey.computed)
+        return domain.https.sslCertificateKey.computed;
+
+    return `${global.nginx.nginxConfigDirectory.computed.replace(/\/+$/, '')}/ssl/${domain.server.domain.computed}.key`;
+};