Pesan Error & Artinya

Ketika kamu menjalankan perintah npm install atau operasi npm lainnya yang memerlukan koneksi ke registry remote, kamu bisa mendapati pesan error yang terasa ambigu dan sulit ditebak akar masalahnya. Berikut adalah kutipan pesan error asli yang muncul di terminal:

npm ERR! code ERR_SSL_CIPHER_OPERATION_FAILED
npm ERR! errno ERR_SSL_CIPHER_OPERATION_FAILED
npm ERR! request to https://registry.npmjs.org/xxx failed, reason: error:0A000152:SSL routines::unsafe legacy renegotiation disabled

Pesan ini bisa juga bervariasi sedikit tergantung versi Node.js dan OpenSSL yang digunakan, namun intinya selalu mengandung kode ERR_SSL_CIPHER_OPERATION_FAILED dan sering kali disertai keterangan tentang unsafe legacy renegotiation disabled.

Arti singkat: Error ini menyatakan bahwa Node.js (melalui library OpenSSL di bawahnya) gagal menyelesaikan handshake SSL/TLS dengan server npm registry. Penyebab langsungnya adalah ketidakcocokan antara konfigurasi cipher/protokol SSL yang digunakan klien (npm + Node.js kamu) dengan yang diizinkan atau disediakan oleh server registry. Dalam banyak kasus, OpenSSL versi 3.x yang dibundel di Node.js 17+ secara default menonaktifkan legacy renegotiation dan cipher lama demi alasan keamanan, sementara jaringan kamu — misalnya karena proxy perusahaan, antivirus yang melakukan SSL interception, atau registry custom yang belum di-update — masih mengandalkan konfigurasi lama tersebut.

Kenapa error ini muncul? Secara ringkas, ada dinding antara npm dan server registry. Dinding itu bisa berupa proxy korporat yang melakukan man-in-the-middle pada koneksi SSL, bisa berupa konfigurasi OpenSSL yang terlalu ketat untuk lingkungan jaringan kamu, bisa berupa versi Node.js yang terlalu baru sehingga menolak cipher legacy, atau bisa juga berupa sertifikat CA kustom yang tidak dikenali. Error ini bukan berarti kodenya salah — ini adalah masalah infrastruktur jaringan dan keamanan di environment kamu.

Penyebab Umum

Berikut adalah root cause yang paling sering ditemui, diurutkan dari yang paling banyak terjadi di lapangan:

  1. Proxy korporat / SSL Inspection (MITM) — Banyak perusahaan menjalankan proxy (Zscaler, Palo Alto, Cisco, F5, dll.) yang memotong koneksi HTTPS, mendekripsi traffic, lalu mengenkripsi ulang menggunakan sertifikat internal. Sertifikat ini sering menggunakan cipher atau renegotiation mode yang sudah dianggap unsafe oleh OpenSSL 3.x. Ini adalah penyebab nomor satu, terutama di lingkungan enterprise.

  2. Node.js versi 17+ dengan OpenSSL 3.x — Mulai Node.js 17, OpenSSL 3.x menjadi default(https://nodejs.org/en/blog/announcements/v17-release-announce). OpenSSL 3 memperketat kebijakan cipher legacy dan renegotiation. Jika kamu baru upgrade dari Node 16 ke Node 17+ tanpa mengubah konfigurasi jaringan, error ini bisa langsung muncul.

  3. Custom/private npm registry dengan TLS versi lama — Organisasi yang menjalankan private registry (Verdaccio, Nexus, Artifactory) terkadang belum mengupdate konfigurasi TLS-nya. Jika registry masih menggunakan TLS 1.0/1.1 atau cipher suite usang, klien Node.js baru akan menolak koneksi.

  4. Antivirus atau firewall lokal yang melakukan HTTPS scanning — Sama seperti proxy korporat, antivirus desktop (Kaspersky, Avast, ESET) sering memiliki fitur "HTTPS inspection" yang meng-inject sertifikat sendiri. Ini merusak chain of trust dan cipher negotiation.

  5. Environment variable NODE_OPTIONS atau .npmrc yang salah konfigurasi — Misalnya kamu pernah mengatur strict-ssl=false atau flag OpenSSL lama yang sekarang tidak kompatibel, atau kamu punya ca/cafile di .npmrc yang sudah expired atau salah format.

Cara Memastikan Penyebab Mana yang Kamu Alami

  • Cek apakah kamu di balik proxy: Jalankan echo $HTTP_PROXY dan echo $HTTPS_PROXY (Linux/Mac) atau echo %HTTP_PROXY% (Windows CMD). Jika ada value, kamu kemungkinan besar di balik proxy korporat.
  • Cek versi Node.js: Jalankan node -v. Jika versi ≥ 17, kamu masuk kategori penyebab nomor 2.
  • Cek registry yang digunakan: Jalankan npm config get registry. Jika bukan https://registry.npmjs.org/, kamu pakai custom registry.
  • Cek antivirus: Nonaktifkan sementara fitur HTTPS scanning di antivirus, lalu coba npm install lagi. Jika berhasil, penyebabnya antivirus.
  • Tes koneksi langsung: Jalankan curl -v https://registry.npmjs.org/ dan perhatikan output TLS. Jika muncul sertifikat yang bukan dari DigiCert (issuer resmi npm), berarti ada SSL interception.

Solusi Step-by-Step

Langkah 1: Identifikasi Versi Node.js dan OpenSSL

Jalankan perintah berikut untuk mengetahui versi yang kamu gunakan:

node -v
node -e "console.log(process.versions.openssl)"

Output yang diharapkan:

v20.11.0
3.0.11+quic

Jika OpenSSL menunjukkan versi 3.x, maka kamu berada di zona rawan error ini. Jika versi OpenSSL 1.1.1, penyebabnya kemungkinan bukan renegotiation cipher melainkan sertifikat atau proxy.

Langkah 2: Cek dan Ekspor Sertifikat Proxy (Jika di Balik Proxy Korporat)

Jika kamu bekerja di perusahaan yang menggunakan proxy dengan SSL inspection, kamu perlu membuat Node.js mengenali sertifikat CA internal perusahaan. Cara paling andal adalah mengekspor sertifikat tersebut dan mendaftarkannya ke npm.

Di Windows, sertifikat proxy biasanya bisa diekspor dari Certificate Manager (certmgr.msc) di kategori Trusted Root Certification Authorities. Cari sertifikat dengan nama organisasimu atau nama vendor proxy. Ekspor sebagai Base64 .crt.

Di Linux/Mac, sering kali sertifikat proxy sudah ada di system bundle. Kamu bisa menemukannya di:

  • Linux (Ubuntu/Debian): /usr/local/share/ca-certificates/ atau /etc/ssl/certs/
  • macOS: Gunakan Keychain Access → System Roots → export sebagai PEM

Setelah mendapat file sertifikat (misalnya corporate-proxy-ca.crt), konfigurasikan npm:

npm config set cafile /path/to/corporate-proxy-ca.crt

Verifikasi: Jalankan npm config get cafile dan pastikan path-nya benar.

Langkah 3: Izinkan Legacy Renegotiation (Solusi Cepat untuk Node.js 17+)

Jika langkah 2 tidak memungkinkan (misalnya kamu tidak punya akses ke sertifikat proxy), kamu bisa menginstruksikan OpenSSL untuk mengizinkan legacy renegotiation melalui environment variable. Ini adalah solusi langsung yang paling sering dipakai:

Linux/macOS (bash/zsh):

export NODE_OPTIONS="--openssl-legacy-provider"
npm install

Windows (CMD):

set NODE_OPTIONS=--openssl-legacy-provider
npm install

Windows (PowerShell):

$env:NODE_OPTIONS="--openssl-legacy-provider"
npm install

Output yang diharapkan: Jika berhasil, npm install akan berjalan normal dan menampilkan daftar paket yang diunduh tanpa error SSL.

Verifikasi solusi berhasil: Setelah npm install selesai, jalankan:

npm ls --depth=0

Jika daftar paket muncul tanpa error, solusi berhasil. Kamu juga bisa menjalankan:

npm ping

Yang seharusnya mengembalikan {} (objek kosong) jika koneksi ke registry berhasil.

Langkah 4: Alternatif — Gunakan Node.js Versi 16 (LTS Sebelumnya)

Jika kamu tidak ingin mengatur NODE_OPTIONS setiap kali, opsi lain adalah downgrade ke Node.js 16 yang menggunakan OpenSSL 1.1.1. Gunakan nvm (Node Version Manager):

nvm install 16
nvm use 16
node -v  # harus menunjukkan v16.x.x
npm install

Ini bukan fix permanen, tetapi solusi langsung yang efektif jika kamu butuh working environment segera tanpa mengubah konfigurasi sistem.

Langkah 5: Nonaktifkan strict-ssl Hanya Jika Perlu (Terakhir)

Sebagai upaya terakhir, kamu bisa menonaktifkan verifikasi SSL sepenuhnya. Peringatan: Ini membuat koneksi rentan terhadap serangan man-in-the-middle. Gunakan hanya di environment development yang terisolasi, jangan pernah di CI/CD production.

npm config set strict-ssl false

Untuk mengembalikan:

npm config set strict-ssl true

Fix Permanen (Bukan Workaround)

Solusi-solusi di langkah sebelumnya yang menggunakan NODE_OPTIONS bersifat workaround — kamu harus mengatur ulang setiap kali membuka terminal baru (kecuali kamu memasukkannya ke shell profile). Berikut adalah fix permanen yang mencegah error ini kembali muncul.

1. Pasang NODE_OPTIONS di Shell Profile

Tambahkan baris berikut ke file profile shell kamu sehingga environment variable selalu terpasang setiap sesi terminal dimulai:

Linux/macOS (bash): Tambahkan ke ~/.bashrc atau ~/.bash_profile:

echo 'export NODE_OPTIONS="--openssl-legacy-provider"' >> ~/.bashrc
source ~/.bashrc

Linux/macOS (zsh): Tambahkan ke ~/.zshrc:

echo 'export NODE_OPTIONS="--openssl-legacy-provider"' >> ~/.zshrc
source ~/.zshrc

Windows: Set sebagai environment variable sistem melalui System Properties → Environment Variables → User variables → New:

  • Variable name: NODE_OPTIONS
  • Variable value: --openssl-legacy-provider

Atau via PowerShell (persisten untuk user):

[System.Environment]::SetEnvironmentVariable("NODE_OPTIONS", "--openssl-legacy-provider", "User")

Setelah ini, buka terminal baru dan jalankan echo $NODE_OPTIONS (atau echo %NODE_OPTIONS% di CMD) untuk memverifikasi.

2. Daftarkan Sertifikat Proxy Secara Permanen di Node.js

Alih-alih menggunakan flag --openssl-legacy-provider, pendekatan yang lebih aman dan lebih proper adalah mendaftarkan sertifikat CA proxy ke Node.js. Ini adalah best practice karena tidak menurunkan keamanan SSL.

Pertama, gabungkan sertifikat proxy dengan bundle CA default Node.js, atau lebih mudah: arahkan npm ke sertifikat proxy secara langsung.

# Set cafile di .npmrc (global)
npm config set cafile /path/to/corporate-proxy-ca.crt

# Verifikasi
npm config list

Isi file .npmrc kamu (di ~/.npmrc) sekarang akan memuat baris:

cafile=/path/to/corporate-proxy-ca.crt

Perbedaan OS: Pada Windows, path menggunakan backslash atau double backslash. Namun npm menerima forward slash juga, jadi ini aman:

cafile=C:/Users/nama/certs/corporate-proxy-ca.crt

Pastikan file sertifikat dalam format PEM (Base64 ASCII), bukan DER (binary). Jika kamu punya format DER, konversikan:

openssl x509 -inform DER -in corporate-proxy-ca.der -outform PEM -out corporate-proxy-ca.crt

3. Fix di Sisi Registry (Jika Kamu Admin Private Registry)

Jika penyebabnya adalah private registry yang menggunakan cipher/TLS versi lama, fix permanen harus dilakukan di sisi server. Berikut rekomendasi konfigurasi untuk beberapa registry populer:

Verdaccio — Di file config.yaml, pastikan bagian https