Nginx server block
Offers low overhead and clear server_name management for static content, reverse proxy and PHP-FPM setups.
Without a panel, adding domains means managing document roots, virtual-host files, configuration tests, DNS and TLS yourself. This guide handles Debian and RHEL families in one script.
WEB="nginx"
mkdir -p /var/www/ornek.com/public_html
nginx -t && systemctl reload nginx
apachectl configtest && systemctl reload httpd
Bulk automation should not accelerate a wrong architecture. Decide customer isolation, resource limits, application roots, mail and DNS ownership first.
Offers low overhead and clear server_name management for static content, reverse proxy and PHP-FPM setups.
Suitable when .htaccess compatibility and shared-hosting-like behavior are required.
Keeping each domain under /var/www/domain/public_html simplifies backups, permissions and deployment.
Dynamic mass virtual hosting may suit thousands of identical domains, but per-file vhosts are safer when isolation or custom settings are needed.
Debian commonly uses sites-available while RHEL commonly uses conf.d; the script detects both.
cat /etc/os-release
nginx -v 2>&1 || httpd -v 2>&1 || apache2 -v Use only Punycode/ASCII domains and one entry per line.
awk 'NF{print tolower($0)}' /root/domainler.txt | sort -u > /root/domainler-temiz.txt Set nginx or apache according to the installed stack.
WEB="nginx" Check file paths, user permissions and default-vhost behavior.
head -n 2 /root/domainler-temiz.txt > /root/domainler-test.txt The script creates directories and vhosts and performs one reload only after a successful config test.
chmod 700 /root/linux-toplu-domain.sh
/root/linux-toplu-domain.sh Verify A/AAAA resolution first, then run Certbot or your chosen ACME client in controlled batches.
dig +short ornek.com A
curl -I http://ornek.com Detects distribution paths, creates document roots and vhosts, tests configuration and reloads.
#!/usr/bin/env bash
set -Eeuo pipefail
WEB="nginx"
DOMAIN_DOSYASI="/root/domainler.txt"
WEB_KOKU="/var/www"
SONUC="/root/linux-domain-sonuclari.csv"
HATA="/root/linux-domain-errorlari.log"
[[ "$EUID" -eq 0 ]] || { echo "root gerekli"; exit 1; }
[[ -f "$DOMAIN_DOSYASI" ]] || { echo "domain listesi bulunamadi"; exit 1; }
if [[ -d /etc/nginx/sites-available ]]; then
NGINX_DIZIN="/etc/nginx/sites-available"
NGINX_ETKIN="/etc/nginx/sites-enabled"
else
NGINX_DIZIN="/etc/nginx/conf.d"
NGINX_ETKIN=""
fi
if [[ -d /etc/apache2/sites-available ]]; then
APACHE_DIZIN="/etc/apache2/sites-available"
APACHE_LOG="/var/log/apache2"
APACHE_SERVIS="apache2"
else
APACHE_DIZIN="/etc/httpd/conf.d"
APACHE_LOG="/var/log/httpd"
APACHE_SERVIS="httpd"
fi
if id -u www-data >/dev/null 2>&1; then
WEB_KULLANICI="www-data"
elif id -u nginx >/dev/null 2>&1; then
WEB_KULLANICI="nginx"
elif id -u apache >/dev/null 2>&1; then
WEB_KULLANICI="apache"
else
WEB_KULLANICI="root"
fi
printf '"domain","web","durum","document_root"\n' > "$SONUC"
: > "$HATA"
chmod 600 "$SONUC" "$HATA"
while IFS= read -r SATIR || [[ -n "$SATIR" ]]; do
DOMAIN="$(printf '%s' "$SATIR" | tr -d '\r' | tr '[:upper:]' '[:lower:]' | sed -E 's#^[[:space:]]+##;s#[[:space:]]+$##;s#^https?://##;s#^www\.##;s#/.*$##')"
[[ -z "$DOMAIN" ]] && continue
[[ "$DOMAIN" == \#* ]] && continue
if [[ ! "$DOMAIN" =~ ^[a-z0-9][a-z0-9.-]*\.[a-z]{2,63}$ ]]; then
printf '"%s","%s","gecersiz",""\n' "$DOMAIN" "$WEB" >> "$SONUC"
continue
fi
KOK="${WEB_KOKU}/${DOMAIN}/public_html"
mkdir -p "$KOK"
printf '<!doctype html><html lang="tr"><meta charset="utf-8"><title>%s</title><h1>%s hazır</h1></html>\n' "$DOMAIN" "$DOMAIN" > "${KOK}/index.html"
chown -R "$WEB_KULLANICI:$WEB_KULLANICI" "${WEB_KOKU}/${DOMAIN}"
chmod -R u=rwX,g=rX,o=rX "${WEB_KOKU}/${DOMAIN}"
if [[ "$WEB" == "nginx" ]]; then
DOSYA="${NGINX_DIZIN}/${DOMAIN}.conf"
cat > "$DOSYA" <<EOF
server {
listen 80;
listen [::]:80;
server_name ${DOMAIN} www.${DOMAIN};
root ${KOK};
index index.html index.php;
access_log /var/log/nginx/${DOMAIN}.access.log;
error_log /var/log/nginx/${DOMAIN}.error.log;
location / {
try_files \$uri \$uri/ /index.php?\$query_string;
}
}
EOF
if [[ -n "$NGINX_ETKIN" ]]; then
ln -sfn "$DOSYA" "${NGINX_ETKIN}/${DOMAIN}.conf"
fi
else
DOSYA="${APACHE_DIZIN}/${DOMAIN}.conf"
cat > "$DOSYA" <<EOF
<VirtualHost *:80>
ServerName ${DOMAIN}
ServerAlias www.${DOMAIN}
DocumentRoot "${KOK}"
ErrorLog "${APACHE_LOG}/${DOMAIN}.error.log"
CustomLog "${APACHE_LOG}/${DOMAIN}.access.log" combined
<Directory "${KOK}">
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
EOF
if command -v a2ensite >/dev/null 2>&1; then
a2ensite "${DOMAIN}.conf" >/dev/null
fi
fi
printf '"%s","%s","hazir","%s"\n' "$DOMAIN" "$WEB" "$KOK" >> "$SONUC"
done < "$DOMAIN_DOSYASI"
if [[ "$WEB" == "nginx" ]]; then
nginx -t
systemctl reload nginx
else
apachectl configtest
systemctl reload "$APACHE_SERVIS"
fi
echo "Tamamlandi: $SONUC"
echo "Errorlar: $HATA"
Ensure server_name and root match the domain.
server {
listen 80;
server_name ornek.com www.ornek.com;
root /var/www/ornek.com/public_html;
index index.html index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}
Grant Require all granted to the DocumentRoot and choose AllowOverride based on the application.
<VirtualHost *:80>
ServerName ornek.com
ServerAlias www.ornek.com
DocumentRoot "/var/www/ornek.com/public_html"
<Directory "/var/www/ornek.com/public_html">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
Measure status codes for the domain list with limited concurrency.
xargs -a /root/domainler.txt -P 10 -I{} sh -c 'printf "%s " "{}"; curl -LksS -o /dev/null -w "%{http_code}\n" --max-time 10 "http://{}"'
The sites-available/sites-enabled layout is common for Nginx and Apache. The web user is usually www-data.
nginx -tapache2ctl configtestsystemctl reload nginx || systemctl reload apache2Nginx and httpd usually use conf.d. SELinux contexts and firewalld rules must also be verified.
nginx -t || httpd -trestorecon -Rv /var/wwwfirewall-cmd --list-servicesLVE and CageFS are usually meaningful with panel integration. On a panel-free stack, explicitly design systemd, PHP-FPM pools and permissions.
cat /etc/cloudlinux-release 2>/dev/null || truesystemctl list-units 'php*fpm*'find /var/www -maxdepth 2 -type d -printf '%u:%g %p\n' | headDuplicate server_name, missing semicolon, invalid include or permissions may be responsible.
The VirtualHost file contains an invalid directive, quote or module dependency.
Document-root ownership, directory execute permission, SELinux context or Apache Directory rules may be wrong.
The hostname may not match the intended vhost or DNS may point to another IP.
DNS may not have propagated, port 80 may be closed, a proxy may be enabled or the challenge path may be routed elsewhere.
No matching error was found.
Application requirements decide. Apache is convenient for .htaccess dependencies; Nginx suits reverse proxy and centralized configuration.
Four hundred vhosts alone are not unusual on modern servers. Measure certificates, log files, worker limits, reload time and real traffic.
Low-traffic sites within the same trust boundary can share a pool. Separate pools are safer for different customers, users or resource limits.
It can be used for similar domains, but explicit server blocks/VirtualHosts are more manageable when roots, certificates or applications differ.
A separate stage is generally safer. Do not send ACME requests before DNS propagation and HTTP access are verified.
Instead of duplicating identical content across hundreds of domains, use a primary domain, canonical URLs and 301 redirects where appropriate.
We implement panel selection, DNS, SSL, web server, security, backup and verification according to your actual server architecture.