Setting Up SSL, CDN, and Website Performance Optimization: A Technical Guide for IT Teams
A practical guide to installing Let's Encrypt SSL, configuring the Cloudflare CDN, and optimizing Core Web Vitals — complete with target metrics and production-ready Nginx configurations.
Imagine a prospective patient opens your clinic's online registration page from Magelang over a 4G connection. The page takes 6.2 seconds to display the form, and 4 out of 10 visitors close the tab before the "Register" button can be clicked. From the audits we conducted on a number of healthcare service websites in DIY and Central Java, the problem pattern is almost always the same: SSL certificates are installed manually and then expire without monitoring, the CDN is enabled but HTML is never cached so TTFB stays at 1.8 seconds, and the hero image is 2.4 MB uncompressed. These three are not cosmetic matters — their impact is direct on registration conversion, patient trust, and monthly bandwidth bills. This article discusses concrete steps for SSL setup, CDN, and performance optimization: the tool versions used, target numbers, and configurations that can be directly applied on a production server.
Step 1: Install Secure and Automatic SSL/TLS
Domain Validated (DV) certificates from Let's Encrypt are more than sufficient for the majority of healthcare service websites, POS, and company profiles. With Certbot 2.11 and the Nginx plugin, issuance via the HTTP-01 challenge completes in under 30 seconds: certbot --nginx -d klinik.example.id -d www.klinik.example.id. The certificate is valid for 90 days, and Certbot installs a systemd timer that runs twice a day with jitter. What is often missed is not issuance, but monitoring: set an alert when the remaining validity period is less than 21 days, and test certbot renew --dry-run whenever there is a change to the web server configuration.
Second, force TLS 1.3 alongside TLS 1.2. TLS 1.3 requires only 1 round-trip handshake, saving about 40–80 ms at 40 ms latency, and removes weak cipher suites from negotiation. After all subdomains are confirmed to use HTTPS, enable HSTS with max-age=31536000; includeSubDomains — do not immediately preload before ensuring no internal subdomain still uses HTTP. Add OCSP stapling so the browser does not need a separate validation request to the CA, and enable session resumption via ssl_session_cache shared:SSL:10m so returning users do not repeat a full handshake.
Third, avoid the classic trap of Cloudflare's "Flexible SSL" mode. This mode leaves the Cloudflare→origin connection as HTTP, triggers a redirect loop when the origin forces HTTPS, and makes internal traffic unencrypted. Use Full (strict) mode with a valid certificate at the origin. After migration, comb through mixed content — images, fonts, or scripts still called via http:// — using the browser Console or a Screaming Frog crawl; a single HTTP asset will break the padlock status and reduce visitor trust.
Step 2: Configure CDN and Cache Rules Properly
A CDN speeds things up because content is served from the nearest PoP. Cloudflare has a Jakarta PoP; RTT from Yogyakarta is around 15–25 ms, while a direct request to an origin in Singapore is around 45–60 ms. However, by default a CDN only caches static extensions (css, js, images). HTML documents are still forwarded to the origin on every request, so TTFB does not change. This is why many teams report "already using a CDN but still slow" — the problem is in the cache policy, not the network.
Create layered Cache Rules. Hashed assets such as /assets/* are given an Edge TTL of 1 year and a Browser TTL of 1 year with the immutable directive. HTML is given a short Edge TTL of 60–300 seconds plus stale-while-revalidate, enough to absorb traffic spikes without serving stale content for too long. Bypass cache for /admin*, /api/*, and all requests carrying session cookies — especially important for SIMRS or SIM Klinik panels that display patient data per user. Verify the result with curl -I https://domain/aset.css and ensure the cf-cache-status: HIT header appears.
The origin configuration below is the minimum baseline: TLS 1.3, HSTS, OCSP stapling, and cache headers consistent with the rules at the edge (Nginx 1.25.1+ because of the http2 on; directive).
server {
listen 443 ssl;
http2 on;
server_name klinik.example.id;
ssl_certificate /etc/letsencrypt/live/klinik.example.id/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/klinik.example.id/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_stapling on;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Cache-Control "public, max-age=300, stale-while-revalidate=86400" always;
}
Once the cache rules are running, tidy up operations: purge the cache via API at the end of the deploy pipeline so new assets are served immediately, enable Brotli and Early Hints, and make sure "Development Mode" is not left active. For websites with many product images (pharmacy catalogs, POS restaurant menus), enable image resizing at the edge so size variants do not need to be created manually on the server.
Step 3: Optimize Core Web Vitals with Numeric Targets
Measure first, do not guess. Core Web Vitals targets at the 75th percentile of field data: LCP ≤ 2.5 seconds, INP ≤ 200 ms, CLS ≤ 0.1. Use PageSpeed Insights for lab data, CrUX or the web-vitals library (v4) for field data. In one clinic website case, an LCP of 6.2 seconds turned out to be 70% contributed by one 2.4 MB hero image and two external fonts that were render-blocking — not a server problem at all.
For images: convert to AVIF or WebP, target hero images under 150 KB, provide srcset for 3 breakpoints, and always set width/height so CLS remains stable. Use lazy loading only for images below the fold; LCP images should instead be given fetchpriority="high" and preloaded. For CSS/JS: bundle, apply defer to non-critical scripts, and audit third-party — chat widgets, ad pixels, and heatmaps often add 300–600 ms to the main thread and directly depress the INP score.
On the backend side, PHP 8.3 with OPcache (opcache.memory_consumption=256, validate_timestamps=0 in production plus a reload on deploy) provides a 20–40% reduction in execution time compared with PHP 7.4. Add Redis for sessions and query caching, then examine the MySQL 8.0 slow query log. A patient list page that originally took 1.2 seconds dropped to 90 ms after the created_at and klinik_id columns were given a composite index and the ORM relationship was changed from N+1 to eager loading.
Best Practices
- Automate the certificate lifecycle: Certbot renewal + expiration alerts 21 days in advance, and test
--dry-runin staging every quarter. - Differentiate HTML and asset TTLs: HTML 60–300 seconds, hashed assets 1 year
immutable; never treat the two the same. - Always use Full (strict) on the CDN: do not enable Flexible SSL, and bypass cache for all authenticated pages.
- Measure with field data: a Lighthouse score of 98 on an office laptop does not guarantee good LCP on a mid-range phone; monitor the p75 from CrUX or RUM.
- Make performance part of the pipeline: run Lighthouse CI on every pull request with an LCP budget < 2.5 s and a maximum bundle size of 200 KB gzip.
FAQ
Is a free Let's Encrypt certificate sufficient for a SIMRS application?
For transport encryption, yes — it is technically equivalent to a paid DV certificate and recognized by all modern browsers. What needs attention is the issuance quota (50 certificates per registered domain per week), the need for wildcards for many subdomains, and the obligation to monitor internally so there is no downtime if renewal fails.
Does the Cloudflare CDN free plan slow down websites in Indonesia?
No, as long as cache rules are configured correctly. What often happens is a cache miss on HTML because of cookies or the application's Set-Cookie header, so every request still calls the origin. Check cf-cache-status and make sure the application does not send cookies on public pages.
What PageSpeed Insights score should be achieved?
Scores are diagnostic and change easily; do not make them a single KPI. Focus on three field metrics at the 75th percentile: LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1. A website with a lab score of 75 but a field LCP of 2.1 s is better than a score of 95 with an LCP of 4 s.
Is it necessary to enable HTTP/3?
Yes, especially for mobile users with high packet loss. Cloudflare enables it by default, while Nginx 1.25+ supports HTTP/3 through the http_v3_module module with listen 443 quic reuseport;. The benefits are felt on unstable connections, not on clean office networks.
Automated SSL, disciplined cache rules, and field-data-driven optimization are three mutually supporting tasks — skipping one makes the other two less impactful. If your team is preparing a healthcare service website, SIMRS/SIM Klinik portal, ERP, POS, or BPJS/SatuSehat bridging integration and needs a performance audit through implementation, we help companies and healthcare facilities in Yogyakarta, DIY, and Magelang through IT consulting and application development services. From a 1–2 day technical audit to production hardening, schedule a brief discussion to map out your system's improvement priorities.
This article is part of the knowledge base of Nugroho Setiawan, an IT consultant in Jogja (Yogyakarta). Related solutions: PilarPOS SIMRS (Hospital Information System) Clinic Management System. Need a free consultation?
Komentar
Belum ada komentar. Jadilah yang pertama!
Saya membantu konsultasi IT & pengembangan aplikasi untuk rumah sakit, klinik, dan bisnis di Yogyakarta (DIY), Magelang, dan sekitarnya.