{"id":169177,"date":"2026-06-19T08:29:26","date_gmt":"2026-06-19T05:29:26","guid":{"rendered":"https:\/\/computingforgeeks.com\/?p=169177"},"modified":"2026-06-19T08:29:26","modified_gmt":"2026-06-19T05:29:26","slug":"install-apache-opensuse-leap","status":"publish","type":"post","link":"https:\/\/computingforgeeks.com\/install-apache-opensuse-leap\/","title":{"rendered":"Install Apache HTTP Server on openSUSE Leap 16"},"content":{"rendered":"<p>openSUSE Leap 16 ships Apache 2.4.66 with the prefork MPM, and the module set is driven entirely from <code>\/etc\/sysconfig\/apache2<\/code>, not from the main <code>httpd.conf<\/code>. That single design choice is what trips up admins arriving from Debian or RHEL: editing <code>httpd.conf<\/code> to load a module does nothing here, because the SUSE helper scripts regenerate the load list from the sysconfig file. This guide shows how to install Apache on openSUSE Leap 16, open the firewall, serve content, enable modules the SUSE way, and ends with the numbers that decide how you tune it.<\/p>\n\n<p>Every command below was run on a live Leap 16 box: a 2 vCPU, 2 GB virtual machine. The throughput and memory figures in the tuning section come from that machine, not from documentation.<\/p>\n\n<p><em>Tested June 2026 on Apache 2.4.66 (openSUSE Leap 16.0).<\/em><\/p>\n\n<h2>Prerequisites<\/h2>\n\n<p>Apache&#8217;s footprint is set by the MPM and the workload, not by the binary. The prefork MPM forks one process per connection, and on the test box each worker measured at 6.9 MB resident. Multiply that by your expected concurrency to size memory: 256 simultaneous prefork workers is roughly 1.8 GB before the application behind Apache uses a byte. For a static site or a reverse proxy front end, 1 vCPU and 1 GB is enough; for a PHP application server under real load, start at 2 vCPU and 2 GB and scale memory with <code>MaxRequestWorkers<\/code>.<\/p>\n\n<ul>\n<li>An openSUSE Leap 16 server with sudo access. The reference box was 2 vCPU \/ 2 GB, which is a floor for following along, not a production target.<\/li>\n<li>A non-root user in the <code>wheel<\/code> group. The <a href=\"https:\/\/computingforgeeks.com\/opensuse-leap-initial-server-setup\/\">initial server setup and hardening<\/a> guide covers that groundwork.<\/li>\n<li>Network access to the openSUSE CDN for <code>zypper<\/code>.<\/li>\n<\/ul>\n\n<h2>Step 1: Install Apache from the OSS repository<\/h2>\n\n<p>Apache is packaged as <code>apache2<\/code> in the default repositories. Installing it pulls in the prefork MPM and the utilities package:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>sudo zypper refresh\nsudo zypper install apache2<\/code><\/pre>\n\n\n<p>The control binaries land in <code>\/usr\/sbin<\/code>, which is not on a normal user&#8217;s <code>PATH<\/code>, so confirm the version through sudo. Note the control command on openSUSE is <code>apachectl<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>sudo apachectl -v<\/code><\/pre>\n\n\n<p>The build identifies itself as the SUSE flavor:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>Server version: Apache\/2.4.66 (Linux\/SUSE)\nServer built:   2026-05-13 23:29:22.000000000 +0000<\/code><\/pre>\n\n\n<h2>Step 2: Start Apache and enable it at boot<\/h2>\n\n<p>The service unit is <code>apache2<\/code>, with <code>httpd<\/code> and <code>apache<\/code> registered as aliases. Enable and start it in one command:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>sudo systemctl enable --now apache2<\/code><\/pre>\n\n\n<p>Confirm the daemon is up. The main process reports as <code>httpd-prefork<\/code>, which names the active MPM:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>systemctl status apache2 --no-pager<\/code><\/pre>\n\n\n<p>The status block shows it running and processing requests:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>\u25cf apache2.service - The Apache Webserver\n     Loaded: loaded (\/usr\/lib\/systemd\/system\/apache2.service; enabled; preset: disabled)\n     Active: active (running) since Fri 2026-06-19 01:20:35 CEST\n   Main PID: 7167 (httpd-prefork)\n     Status: \"Processing requests...\"<\/code><\/pre>\n\n\n<h2>Step 3: Open the firewall<\/h2>\n\n<p>Leap 16 runs firewalld. Add the HTTP and HTTPS services and reload:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>sudo firewall-cmd --permanent --add-service=http\nsudo firewall-cmd --permanent --add-service=https\nsudo firewall-cmd --reload<\/code><\/pre>\n\n\n<p>List the active services to confirm both are allowed:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>sudo firewall-cmd --list-services<\/code><\/pre>\n\n\n<p>The output includes <code>http<\/code> and <code>https<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>cockpit dhcpv6-client http https ssh<\/code><\/pre>\n\n\n<p>With the firewall open, the server is reachable. All that remains to serve a page is content in the document root.<\/p>\n\n<h2>Step 4: Serve a page from the document root<\/h2>\n\n<p>Apache&#8217;s document root on openSUSE is <code>\/srv\/www\/htdocs<\/code>, set in <code>\/etc\/apache2\/default-server.conf<\/code>, not the <code>\/var\/www\/html<\/code> you may know from other distributions. Drop an index file there and Apache serves it:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>echo '&lt;h1&gt;Apache is running on openSUSE Leap 16&lt;\/h1&gt;' | sudo tee \/srv\/www\/htdocs\/index.html<\/code><\/pre>\n\n\n<p>Open the server&#8217;s address in a browser and the page loads.<\/p>\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"720\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/06\/wm-apache-welcome-page-opensuse-leap.png\" alt=\"Apache web server welcome page in browser on openSUSE Leap 16\" class=\"wp-image-169170\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/06\/wm-apache-welcome-page-opensuse-leap.png 1280w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/06\/wm-apache-welcome-page-opensuse-leap-300x169.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/06\/wm-apache-welcome-page-opensuse-leap-1024x576.png 1024w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/06\/wm-apache-welcome-page-opensuse-leap-768x432.png 768w\" sizes=\"auto, (max-width: 1280px) 100vw, 1280px\" \/><\/figure>\n\n\n<h2>Step 5: Enable modules the SUSE way<\/h2>\n\n<p>This is the part that behaves differently from every other distribution. The list of loaded modules lives in the <code>APACHE_MODULES<\/code> variable inside <code>\/etc\/sysconfig\/apache2<\/code>. You do not edit that file by hand; the <code>a2enmod<\/code> and <code>a2dismod<\/code> helpers edit it for you, then a restart regenerates the load directives. Enable the rewrite module:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>sudo a2enmod rewrite\nsudo systemctl restart apache2<\/code><\/pre>\n\n\n<p>Verifying that the module loaded exposes a real trap. Running <code>httpd -M<\/code> directly returns nothing useful, because the raw binary does not source the SUSE environment that defines the module paths. Use <code>apachectl -M<\/code> instead, which sources <code>\/etc\/sysconfig\/apache2<\/code> first:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>sudo apachectl -M | grep rewrite<\/code><\/pre>\n\n\n<p>With the environment sourced, the module shows as loaded:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>rewrite_module (shared)<\/code><\/pre>\n\n\n<h2>Step 6: Configure a name-based virtual host<\/h2>\n\n<p>Apache reads per-site configuration from <code>\/etc\/apache2\/vhosts.d\/*.conf<\/code>, pulled in by <code>IncludeOptional \/etc\/apache2\/vhosts.d\/*.conf<\/code> in the main config. The directory ships with <code>vhost.template<\/code> and <code>vhost-ssl.template<\/code> as starting points.<\/p>\n\n<p>The site name and web root repeat across the next commands, so set them once as variables and adjust to your real domain:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>export SITE_DOMAIN=\"site1.example.com\"\nexport WEB_ROOT=\"\/srv\/www\/vhosts\/${SITE_DOMAIN}\"<\/code><\/pre>\n\n\n<p>Create the document root and a landing page:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>sudo mkdir -p \"${WEB_ROOT}\"\necho \"&lt;h1&gt;${SITE_DOMAIN} on Apache \/ openSUSE Leap 16&lt;\/h1&gt;\" | sudo tee \"${WEB_ROOT}\/index.html\"<\/code><\/pre>\n\n\n<p>Open a new virtual host file:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>sudo vim \/etc\/apache2\/vhosts.d\/site1.example.com.conf<\/code><\/pre>\n\n\n<p>Add the virtual host. Keep the paths in line with the variables set above:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>&lt;VirtualHost *:80&gt;\n    ServerName site1.example.com\n    ServerAlias www.site1.example.com\n    DocumentRoot \/srv\/www\/vhosts\/site1.example.com\n\n    &lt;Directory \/srv\/www\/vhosts\/site1.example.com&gt;\n        Require all granted\n        Options FollowSymLinks\n        AllowOverride All\n    &lt;\/Directory&gt;\n\n    ErrorLog \/var\/log\/apache2\/site1.example.com-error.log\n    CustomLog \/var\/log\/apache2\/site1.example.com-access.log combined\n&lt;\/VirtualHost&gt;<\/code><\/pre>\n\n\n<p>Validate the syntax before restarting:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>sudo apachectl configtest<\/code><\/pre>\n\n\n<p>A clean configuration reports <code>Syntax OK<\/code>. On a fresh install it also prints one warning that confuses people:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>AH00558: httpd-prefork: Could not reliably determine the server's fully qualified domain name, using localhost.localdomain. Set the 'ServerName' directive globally to suppress this message\nSyntax OK<\/code><\/pre>\n\n\n<p>The warning is harmless, but suppress it by setting a global ServerName. Create a small drop-in:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>echo \"ServerName localhost\" | sudo tee \/etc\/apache2\/conf.d\/servername.conf<\/code><\/pre>\n\n\n<p>Restart to apply the virtual host:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>sudo systemctl restart apache2<\/code><\/pre>\n\n\n<p>Confirm the virtual host answers for its name by sending a matching Host header:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>curl -s -H \"Host: ${SITE_DOMAIN}\" http:\/\/127.0.0.1\/<\/code><\/pre>\n\n\n<p>Apache returns the virtual host&#8217;s page:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>&lt;h1&gt;site1.example.com on Apache \/ openSUSE Leap 16&lt;\/h1&gt;<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"940\" height=\"600\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/06\/wm-apache-version-status-opensuse-leap.png\" alt=\"Apache version, systemctl status and a2enmod on openSUSE Leap 16 terminal\" class=\"wp-image-169169\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/06\/wm-apache-version-status-opensuse-leap.png 940w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/06\/wm-apache-version-status-opensuse-leap-300x191.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/06\/wm-apache-version-status-opensuse-leap-768x490.png 768w\" sizes=\"auto, (max-width: 940px) 100vw, 940px\" \/><\/figure>\n\n\n<h2>A note on SELinux<\/h2>\n\n<p>Leap 16 ships SELinux in enforcing mode, and Apache runs in the confined <code>httpd_t<\/code> domain. In practice the targeted policy shipped with Leap is far more permissive than the RHEL equivalent: <code>getsebool -a<\/code> returns an empty list, so there are none of the <code>httpd_*<\/code> toggles you would flip on Rocky or RHEL, and content under <code>\/srv\/www<\/code> serves without any relabeling. Keep document roots under <code>\/srv\/www<\/code> and SELinux stays out of the way. If a future policy update tightens this, relabel a custom path with <code>semanage fcontext -a -t httpd_sys_content_t<\/code> followed by <code>restorecon<\/code>.<\/p>\n\n<h2>Performance: the numbers that set the tuning<\/h2>\n\n<p>The default prefork MPM forks one process per connection. On the 2 vCPU test box, <code>ab -n 5000 -c 50<\/code> against the static document root measured 11,507 requests per second with zero failed requests, a 4.3 ms mean response time, and a 10.2 MB\/s transfer rate. Each prefork worker held 6.9 MB resident. Those two numbers, requests per second and memory per worker, are what you tune against.<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>ab -n 5000 -c 50 http:\/\/127.0.0.1\/<\/code><\/pre>\n\n\n<p>The relevant lines from the run:<\/p>\n\n\n<pre class=\"wp-block-code code\"><code>Complete requests:      5000\nFailed requests:        0\nRequests per second:    11507.72 [#\/sec] (mean)\nTime per request:       4.345 [ms] (mean)\nTransfer rate:          10159.16 [Kbytes\/sec] received<\/code><\/pre>\n\n\n<p>Two levers move those figures. First, <code>MaxRequestWorkers<\/code> in <code>\/etc\/apache2\/server-tuning.conf<\/code> caps concurrency; with prefork at 6.9 MB per worker, set it from your available RAM, not from a round number, or a traffic spike will push the box into swap. Second, the MPM itself. Prefork trades memory for stability and is the safe default for non-thread-safe modules like older PHP handlers. For high-concurrency keep-alive traffic, the event MPM holds far more idle connections per megabyte; swap it in by installing <code>apache2-event<\/code> (which replaces <code>apache2-prefork<\/code>) and restarting. Measure both on your own workload before committing, because the right MPM depends on whether your bottleneck is memory or thread safety.<\/p>\n\n<p>With Apache serving and the MPM chosen, the next steps are usually putting it behind TLS and managing it from the browser. <a href=\"https:\/\/computingforgeeks.com\/cockpit-web-console-opensuse-leap\/\">Cockpit<\/a> exposes the service state and logs, and the <a href=\"https:\/\/computingforgeeks.com\/things-to-do-after-installing-opensuse-leap\/\">post-install guide<\/a> plus the <a href=\"https:\/\/computingforgeeks.com\/zypper-command-cheat-sheet\/\">zypper reference<\/a> cover the surrounding setup.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>openSUSE Leap 16 ships Apache 2.4.66 with the prefork MPM, and the module set is driven entirely from \/etc\/sysconfig\/apache2, not from the main httpd.conf. That single design choice is what trips up admins arriving from Debian or RHEL: editing httpd.conf to load a module does nothing here, because the SUSE helper scripts regenerate the load &#8230; <a title=\"Install Apache HTTP Server on openSUSE Leap 16\" class=\"read-more\" href=\"https:\/\/computingforgeeks.com\/install-apache-opensuse-leap\/\" aria-label=\"Read more about Install Apache HTTP Server on openSUSE Leap 16\">Read more<\/a><\/p>\n","protected":false},"author":21,"featured_media":169176,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[299,47,50,349],"tags":[282,9986,21787],"cfg_series":[39887],"class_list":["post-169177","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to","category-linux","category-linux-tutorials","category-web-hosting","tag-linux","tag-opensuse","tag-web-server","cfg_series-opensuse-leap-16"],"_links":{"self":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/169177","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/comments?post=169177"}],"version-history":[{"count":2,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/169177\/revisions"}],"predecessor-version":[{"id":169218,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/169177\/revisions\/169218"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/media\/169176"}],"wp:attachment":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/media?parent=169177"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/categories?post=169177"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/tags?post=169177"},{"taxonomy":"cfg_series","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/cfg_series?post=169177"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}