Chat
Ask me anything
Ithy Logo
Sign in to save

Giải mã lỗi SSL "Unable to Get Local Issuer Certificate" khi gọi Google API trong CodeIgniter 4

Khắc phục triệt để sự cố xác thực chứng chỉ SSL để kết nối an toàn với Google OAuth.

fix-codeigniter-ssl-error-60-tcwp936d

Khi phát triển ứng dụng CodeIgniter 4 cần tương tác với các dịch vụ bên ngoài qua HTTPS, chẳng hạn như API Google OAuth để làm mới token, bạn có thể gặp phải lỗi khó chịu: cURL error 60: SSL certificate problem: unable to get local issuer certificate. Lỗi này xuất hiện trong đoạn mã tương tự như sau:

$client = \Config\Services::curlrequest();
$response = $client->request('POST', 'https://oauth2.googleapis.com/token', [
    'form_params' => [
        'client_id' => $this->clientId,
        'client_secret' => $this->clientSecret,
        'refresh_token' => $this->refreshToken,
        'grant_type' => 'refresh_token',
    ]
]);
// => GuzzleHttp\Exception\RequestException #60: SSL certificate problem: unable to get local issuer certificate

Lỗi này không phải do logic code của bạn sai, mà là vấn đề liên quan đến cấu hình môi trường máy chủ hoặc PHP, khiến nó không thể xác minh tính hợp lệ của chứng chỉ SSL do máy chủ Google cung cấp. Bài viết này sẽ đi sâu vào nguyên nhân và cung cấp các giải pháp chi tiết, an toàn để khắc phục lỗi này.

Điểm Nổi Bật

  • Nguyên nhân cốt lõi: Lỗi xảy ra do PHP/cURL trên máy chủ của bạn thiếu hoặc không tìm thấy bộ chứng chỉ gốc (CA Bundle) đáng tin cậy để xác thực chứng chỉ SSL của Google.
  • Giải pháp chính (khuyến nghị): Cập nhật tệp chứng chỉ CA (cacert.pem) mới nhất và cấu hình php.ini để trỏ đúng đường dẫn đến tệp này (curl.cainfo và openssl.cafile).
  • Giải pháp thay thế (CodeIgniter): Cấu hình trực tiếp đường dẫn tệp cacert.pem trong tùy chọn verify khi sử dụng thư viện HTTP Client của CodeIgniter 4.

Hiểu Rõ Lỗi SSL Certificate Error 60

Tại sao kết nối HTTPS thất bại?

Giao thức HTTPS dựa trên SSL/TLS để mã hóa dữ liệu và xác thực danh tính máy chủ. Quá trình này bao gồm "bắt tay SSL" (SSL Handshake), trong đó máy chủ (Google) trình bày chứng chỉ SSL của mình cho client (ứng dụng CodeIgniter của bạn). Client sau đó cần xác minh chứng chỉ này dựa trên một danh sách các Tổ chức phát hành chứng chỉ (Certificate Authorities - CAs) gốc mà nó tin tưởng.

Lỗi "unable to get local issuer certificate" có nghĩa là client không thể tìm thấy CA gốc đã cấp chứng chỉ cho Google trong kho lưu trữ tin cậy cục bộ của nó, hoặc không thể xây dựng được chuỗi tin cậy từ chứng chỉ của Google về một CA gốc đáng tin cậy. Điều này dẫn đến việc kết nối bị từ chối vì không đảm bảo an toàn.

Sơ đồ tóm tắt quá trình bắt tay SSL

Quá trình bắt tay SSL yêu cầu client xác minh chứng chỉ của server dựa trên các CA gốc tin cậy.

Các Nguyên Nhân Phổ Biến

Lỗi này thường xuất phát từ một trong các lý do sau:

  • Thiếu hoặc Lỗi Thời Tệp Chứng Chỉ CA (`cacert.pem`): Đây là nguyên nhân phổ biến nhất. Môi trường PHP/cURL của bạn cần một tệp chứa danh sách các CA gốc công cộng đáng tin cậy (thường gọi là `cacert.pem` hoặc CA bundle). Nếu tệp này không tồn tại, bị lỗi thời (không chứa CA mới), hoặc không thể truy cập được, quá trình xác minh sẽ thất bại.
  • Cấu Hình `php.ini` Sai: Tệp cấu hình `php.ini` chứa các chỉ thị `curl.cainfo` và `openssl.cafile` để chỉ định đường dẫn đến tệp CA bundle. Nếu các đường dẫn này sai, bị comment (có dấu `;` ở đầu), hoặc trỏ đến một tệp không tồn tại/không đọc được, PHP sẽ không tìm thấy chứng chỉ cần thiết. Lưu ý rằng có thể có nhiều tệp `php.ini` (cho web server và CLI), bạn cần chỉnh sửa đúng tệp đang được sử dụng.
  • Extension PHP Chưa Được Bật: Các extension `php_openssl` và `php_curl` cần được kích hoạt trong `php.ini` để xử lý các kết nối HTTPS và sử dụng cURL.
  • Vấn Đề Môi Trường:
    • Thời gian hệ thống sai: Nếu đồng hồ trên máy chủ của bạn không chính xác, nó có thể gây ra lỗi xác thực chứng chỉ vì chứng chỉ có thời hạn hiệu lực.
    • Firewall/Proxy: Tường lửa hoặc máy chủ proxy có thể chặn hoặc can thiệp vào kết nối SSL.
    • Phần mềm diệt virus: Một số phần mềm diệt virus (như Avast, Norton) có tính năng quét HTTPS, đôi khi có thể gây cản trở việc xác thực chứng chỉ.
  • Sử dụng chứng chỉ tự ký (ít liên quan đến Google API): Nếu kết nối đến một máy chủ nội bộ sử dụng chứng chỉ tự ký, bạn cần thêm CA gốc của chứng chỉ đó vào kho tin cậy của client.

Giải Pháp Khắc Phục Lỗi

Dưới đây là các phương pháp để giải quyết lỗi "unable to get local issuer certificate", bắt đầu từ giải pháp được khuyến nghị nhất.

Giải pháp 1: Cập Nhật CA Bundle và Cấu Hình `php.ini` (Khuyến Nghị)

Đây là cách tiếp cận chuẩn mực và an toàn nhất, đảm bảo rằng toàn bộ môi trường PHP của bạn có thể xác thực các kết nối SSL đúng cách.

Bước 1: Tải Tệp Chứng Chỉ CA Mới Nhất

Tải tệp `cacert.pem` (CA certificate bundle) mới nhất từ một nguồn đáng tin cậy. Trang web chính thức của cURL là lựa chọn tốt nhất:

Bước 2: Lưu Tệp `cacert.pem` Trên Máy Chủ

Lưu tệp vừa tải vào một vị trí cố định và dễ truy cập trên máy chủ của bạn. Ví dụ:

  • Windows (XAMPP/WAMP): `C:\php\extras\ssl\cacert.pem` hoặc `C:\xampp\php\extras\ssl\cacert.pem` (bạn có thể cần tạo thư mục `extras\ssl`).
  • Linux: `/etc/ssl/certs/cacert.pem` hoặc một đường dẫn tùy chỉnh như `/opt/php-certs/cacert.pem`.
  • Trong thư mục dự án (ít khuyến khích hơn cho cấu hình global): `/path/to/your-codeigniter-project/certs/cacert.pem`.

Đảm bảo quyền đọc cho tệp này đối với user mà web server (Apache, Nginx) hoặc PHP-FPM đang chạy.

Bước 3: Cấu Hình `php.ini`

Xác định vị trí tệp `php.ini` đang hoạt động. Bạn có thể:

  • Tạo một tệp PHP với nội dung `` và truy cập qua trình duyệt để xem "Loaded Configuration File".
  • Chạy lệnh `php --ini` trong terminal.

Mở tệp `php.ini` đó và tìm các dòng sau. Bỏ dấu chấm phẩy (`;`) ở đầu nếu có và cập nhật đường dẫn tuyệt đối đến tệp `cacert.pem` bạn đã lưu:

; Tìm dòng này (hoặc thêm nếu không có) trong phần [curl]
curl.cainfo = "C:\php\extras\ssl\cacert.pem"  ; Ví dụ cho Windows
; hoặc
; curl.cainfo = "/etc/ssl/certs/cacert.pem" ; Ví dụ cho Linux

; Tìm dòng này (hoặc thêm nếu không có) trong phần [openssl]
openssl.cafile = "C:\php\extras\ssl\cacert.pem" ; Ví dụ cho Windows
; hoặc
; openssl.cafile = "/etc/ssl/certs/cacert.pem" ; Ví dụ cho Linux

Lưu ý: Đảm bảo đường dẫn được đặt trong dấu ngoặc kép và sử dụng đúng dấu phân cách thư mục cho hệ điều hành của bạn (`\` cho Windows, `/` cho Linux/macOS).

Bước 4: Kích hoạt Extensions (Nếu cần)

Trong cùng tệp `php.ini`, đảm bảo các extension sau không bị comment:

extension=php_openssl ; hoặc extension=openssl.so trên Linux
extension=php_curl    ; hoặc extension=curl.so trên Linux

Bước 5: Khởi Động Lại Dịch Vụ

Lưu các thay đổi trong `php.ini` và khởi động lại máy chủ web (Apache, Nginx) hoặc dịch vụ PHP-FPM để các thay đổi có hiệu lực.

Giải pháp 2: Cấu Hình Trực Tiếp Trong CodeIgniter 4

Nếu bạn không có quyền truy cập để sửa `php.ini` (ví dụ: trên shared hosting) hoặc muốn cấu hình chỉ áp dụng cho yêu cầu cụ thể này, bạn có thể chỉ định đường dẫn đến tệp `cacert.pem` trực tiếp trong tùy chọn của `CURLRequest`.

Trước tiên, hãy đảm bảo bạn đã tải và lưu tệp `cacert.pem` vào một nơi trong dự án của bạn (ví dụ: `WRITEPATH . 'certs/cacert.pem'`). Sau đó, sử dụng tùy chọn `verify`:

$client = \Config\Services::curlrequest([
    'baseURI' => 'https://oauth2.googleapis.com/', // Optional base URI
]);

// Xác định đường dẫn tuyệt đối đến file cacert.pem
// Ví dụ lưu trong thư mục writable/certs của CodeIgniter
$caCertPath = WRITEPATH . 'certs/cacert.pem'; 

if (!is_readable($caCertPath)) {
    // Xử lý lỗi nếu không tìm thấy hoặc không đọc được file chứng chỉ
    log_message('error', 'CA Certificate file not found or not readable: ' . $caCertPath);
    // throw new \Exception('Required CA certificate file is missing.'); 
}

$response = $client->request('POST', 'token', [ // 'token' là đường dẫn tương đối nếu đã set baseURI
    'form_params' => [
        'client_id' => $this->clientId,
        'client_secret' => $this->clientSecret,
        'refresh_token' => $this->refreshToken,
        'grant_type' => 'refresh_token',
    ],
    'verify' => $caCertPath // Chỉ định đường dẫn đến tệp cacert.pem
]);

// Hoặc sử dụng setOption nếu bạn không muốn truyền vào mỗi request
// $client->setOption(CURLOPT_CAINFO, $caCertPath);
// $response = $client->request(...);

Phương pháp này ghi đè cài đặt mặc định và buộc cURL sử dụng tệp chứng chỉ bạn chỉ định cho yêu cầu này.

Giải pháp 3: Vô Hiệu Hóa Xác Thực SSL (Không Khuyến Nghị, Chỉ Dùng Tạm Thời)

Cảnh báo: Giải pháp này loại bỏ việc kiểm tra chứng chỉ SSL, làm mất đi lớp bảo mật quan trọng của HTTPS. Dữ liệu của bạn có thể bị lộ hoặc bị tấn công "man-in-the-middle". Chỉ sử dụng phương pháp này trong môi trường phát triển cục bộ để gỡ lỗi tạm thời và không bao giờ triển khai lên môi trường production.

Để tắt xác thực, đặt tùy chọn `verify` thành `false`:

$client = \Config\Services::curlrequest();

$response = $client->request('POST', 'https://oauth2.googleapis.com/token', [
    'form_params' => [
        'client_id' => $this->clientId,
        'client_secret' => $this->clientSecret,
        'refresh_token' => $this->refreshToken,
        'grant_type' => 'refresh_token',
    ],
    'verify' => false // TẮT XÁC THỰC SSL - KHÔNG AN TOÀN!
]);

Nếu bạn buộc phải dùng cách này trong quá trình phát triển, hãy đảm bảo chuyển về Giải pháp 1 hoặc 2 trước khi đưa ứng dụng lên môi trường thực tế.


Sơ Đồ Tổng Quan Khắc Phục Lỗi

Mindmap Các Bước Chính

Mindmap dưới đây tóm tắt các nguyên nhân và giải pháp chính cho lỗi SSL Error 60 khi làm việc với CodeIgniter 4 và Google API:

mindmap root["Lỗi SSL Error 60 (CodeIgniter 4 & Google API)"] id1["Nguyên Nhân"] id1a["Thiếu/Lỗi thời cacert.pem"] id1b["Sai cấu hình php.ini (curl.cainfo, openssl.cafile)"] id1c["Chưa bật extension (openssl, curl)"] id1d["Vấn đề môi trường (Thời gian, Firewall, Antivirus)"] id2["Giải Pháp"] id2a["Cập nhật cacert.pem & Cấu hình php.ini (Khuyến nghị)"] id2a1["Tải cacert.pem từ curl.se"] id2a2["Lưu vào vị trí cố định"] id2a3["Sửa php.ini (curl.cainfo, openssl.cafile)"] id2a4["Bật extensions"] id2a5["Restart web server/PHP-FPM"] id2b["Cấu hình trong CodeIgniter"] id2b1["Lưu cacert.pem trong dự án"] id2b2["Sử dụng option 'verify' => '/path/to/cacert.pem'"] id2b3["Hoặc setOption(CURLOPT_CAINFO, ...)"] id2c["Tắt xác thực SSL (Không an toàn)"] id2c1["Sử dụng option 'verify' => false"] id2c2["Chỉ dùng cho môi trường dev tạm thời"] id3["Kiểm Tra & Xác Minh"] id3a["Chạy lại code"] id3b["Kiểm tra phpinfo()"] id3c["Sử dụng curl command line"] id3d["Kiểm tra log lỗi server"] id3e["Kiểm tra thời gian, firewall, antivirus"]

So Sánh Các Giải Pháp

Đánh giá dựa trên các tiêu chí quan trọng

Biểu đồ radar dưới đây so sánh 3 giải pháp dựa trên các yếu tố: Mức độ Bảo mật, Tính Dễ Thực Hiện, Độ Ổn Định Lâu Dài, Sự Phụ Thuộc Môi Trường và Tác Động Hiệu Năng (thường không đáng kể cho việc xác thực SSL).

Như biểu đồ cho thấy, việc cập nhật `php.ini` (Giải pháp 1) là lựa chọn cân bằng và an toàn nhất cho môi trường production. Cấu hình trong code (Giải pháp 2) là một giải pháp thay thế tốt khi cần linh hoạt hoặc không sửa được `php.ini`. Tuyệt đối tránh Giải pháp 3 trong môi trường thực tế.


Bảng Tóm Tắt Cấu Hình

Các chỉ thị và tùy chọn quan trọng

Bảng dưới đây tóm tắt các cấu hình chính bạn cần quan tâm:

Cấu hình Vị trí Mô tả Ví dụ Giá trị
curl.cainfo `php.ini` (phần `[curl]`) Đường dẫn đến tệp CA bundle cho extension cURL. "C:\php\extras\ssl\cacert.pem" hoặc "/etc/ssl/certs/cacert.pem"
openssl.cafile `php.ini` (phần `[openssl]`) Đường dẫn đến tệp CA bundle cho extension OpenSSL. "C:\php\extras\ssl\cacert.pem" hoặc "/etc/ssl/certs/cacert.pem"
verify Tùy chọn trong `request()` của CodeIgniter HTTP Client Chỉ định đường dẫn tệp CA bundle hoặc bật/tắt xác thực SSL cho yêu cầu cụ thể. '/path/to/cacert.pem' (bật và chỉ định file) hoặc true (dùng mặc định hệ thống) hoặc false (tắt - không an toàn)
CURLOPT_CAINFO Tùy chọn cURL (dùng với `setOption()`) Tương đương `curl.cainfo` nhưng đặt qua mã nguồn cho phiên cURL cụ thể. WRITEPATH . 'certs/cacert.pem'
CURLOPT_SSL_VERIFYPEER Tùy chọn cURL (dùng với `setOption()`) Bật/tắt việc xác minh chứng chỉ SSL của máy chủ ngang hàng. true (mặc định/an toàn) hoặc false (không an toàn)
CURLOPT_SSL_VERIFYHOST Tùy chọn cURL (dùng với `setOption()`) Kiểm tra xem tên chung (Common Name) hoặc Subject Alternative Name trong chứng chỉ có khớp với tên máy chủ đang kết nối không. (Giá trị 2 là khuyến nghị). 2 (khuyến nghị/an toàn) hoặc 0 (không an toàn)

Video Hướng Dẫn Thực Tế

Xem cách khắc phục lỗi cURL error 60

Video dưới đây minh họa các bước cơ bản để khắc phục lỗi "cURL error 60: SSL certificate problem: unable to get local issuer certificate", bao gồm việc tải tệp `cacert.pem`. Mặc dù không dành riêng cho CodeIgniter, nhưng các bước cốt lõi về quản lý chứng chỉ CA là tương tự và hữu ích.

Video hướng dẫn tải `cacert.pem` để khắc phục lỗi cURL error 60.


Kiểm Tra và Gỡ Lỗi Thêm

Các bước xác minh và tìm lỗi

  • Chạy lại mã CodeIgniter: Sau khi áp dụng các thay đổi (đặc biệt là Giải pháp 1), hãy chạy lại đoạn mã gọi API Google.
  • Kiểm tra `phpinfo()`: Đảm bảo các giá trị `curl.cainfo`, `openssl.cafile` và các extension `curl`, `openssl` đã được cập nhật và kích hoạt đúng như mong đợi sau khi sửa `php.ini` và khởi động lại server.
  • Sử dụng cURL trên Command Line: Mở terminal hoặc command prompt trên chính máy chủ đang chạy CodeIgniter và thực hiện lệnh sau:
    curl -v https://oauth2.googleapis.com/token
    Tùy chọn `-v` (verbose) sẽ hiển thị chi tiết quá trình kết nối SSL. Nếu lệnh này cũng báo lỗi tương tự, vấn đề chắc chắn nằm ở cấu hình hệ thống/PHP. Nếu lệnh này thành công, vấn đề có thể nằm ở cách CodeIgniter/Guzzle gọi cURL hoặc đường dẫn chứng chỉ trong code.
    Kết quả lệnh curl -v cho thấy lỗi xác thực chứng chỉ

    Kết quả từ `curl -v` có thể chỉ rõ lỗi xác thực chứng chỉ.

  • Kiểm tra đường dẫn tệp: Nếu sử dụng Giải pháp 2, hãy kiểm tra kỹ đường dẫn đến `cacert.pem` trong code. Sử dụng hàm `realpath()` hoặc `is_readable()` trong PHP để đảm bảo đường dẫn đúng và tệp có thể đọc được.
  • Kiểm tra thời gian hệ thống: Đảm bảo ngày giờ và múi giờ trên máy chủ được đồng bộ chính xác.
  • Kiểm tra Firewall/Antivirus: Tạm thời vô hiệu hóa tường lửa hoặc phần mềm diệt virus để xem có phải chúng là nguyên nhân gây chặn kết nối không.
  • Xem Log Lỗi: Kiểm tra log lỗi của PHP và máy chủ web (Apache error log, Nginx error log) để tìm thêm thông tin chi tiết về lỗi SSL.

Câu Hỏi Thường Gặp (FAQ)

▶ Tệp chứng chỉ CA (`cacert.pem`) là gì?

Đó là một tệp văn bản chứa danh sách các chứng chỉ gốc và trung gian từ các Tổ chức phát hành chứng chỉ (Certificate Authorities - CAs) công cộng đáng tin cậy. Khi client (như PHP/cURL) kết nối đến một máy chủ HTTPS, nó sử dụng tệp này để xác minh rằng chứng chỉ SSL của máy chủ được cấp bởi một CA đáng tin cậy, tạo thành một "chuỗi tin cậy". Nếu không có tệp này hoặc tệp bị lỗi thời, client không thể xác minh và sẽ báo lỗi.

▶ Tại sao không nên vô hiệu hóa xác thực SSL ('verify' => false)?

Vô hiệu hóa xác thực SSL (`verify' => false` hoặc `CURLOPT_SSL_VERIFYPEER => false`) đồng nghĩa với việc client bỏ qua việc kiểm tra tính hợp lệ của chứng chỉ máy chủ. Điều này tạo ra lỗ hổng bảo mật nghiêm trọng:

  • Tấn công Man-in-the-Middle (MITM): Kẻ tấn công có thể chặn kết nối, giả mạo máy chủ Google và đánh cắp dữ liệu nhạy cảm (như client ID, client secret, refresh token) mà ứng dụng của bạn không hề hay biết.
  • Kết nối đến máy chủ không đáng tin cậy: Ứng dụng có thể vô tình gửi dữ liệu đến một máy chủ giả mạo hoặc không an toàn.

Đây là lý do tại sao giải pháp này chỉ nên dùng tạm thời khi gỡ lỗi cục bộ và không bao giờ được phép tồn tại trong môi trường production.

▶ Tôi nên chỉnh sửa tệp `php.ini` nào?

PHP có thể sử dụng nhiều tệp `php.ini` khác nhau tùy thuộc vào cách nó được chạy:

  • Qua Web Server (Apache, Nginx): Thường có một tệp `php.ini` riêng cho module PHP của web server (ví dụ: `php.ini` trong thư mục cấu hình Apache hoặc PHP-FPM). Bạn có thể tìm đường dẫn chính xác bằng cách tạo tệp `phpinfo.php` với nội dung `` và xem mục "Loaded Configuration File".
  • Qua Command Line Interface (CLI): Khi chạy PHP từ terminal (ví dụ: `php your_script.php` hoặc các lệnh `composer`), nó sử dụng một tệp `php.ini` khác, thường được chỉ định trong kết quả của lệnh `php --ini`.

Để khắc phục lỗi khi gọi API từ ứng dụng web CodeIgniter, bạn cần chỉnh sửa tệp `php.ini` mà web server đang sử dụng. Đừng quên khởi động lại web server sau khi sửa đổi.

▶ Việc cập nhật `cacert.pem` có ảnh hưởng đến các kết nối HTTPS khác không?

Có, và đó thường là điều tốt. Khi bạn cập nhật `cacert.pem` và cấu hình `php.ini` (Giải pháp 1), thay đổi này sẽ áp dụng cho tất cả các kết nối HTTPS được thực hiện bởi PHP/cURL trên máy chủ đó (trừ khi bị ghi đè bởi cấu hình trong code). Điều này giúp đảm bảo rằng máy chủ của bạn có thể xác thực đúng cách các chứng chỉ SSL từ nhiều dịch vụ khác nhau, không chỉ Google, miễn là chúng được cấp bởi các CA công cộng có trong tệp bundle mới.

▶ Tôi đã cập nhật `cacert.pem` và `php.ini` nhưng vẫn bị lỗi?

Nếu bạn đã làm theo Giải pháp 1 mà vẫn gặp lỗi, hãy kiểm tra lại các điểm sau:

  • Đã sửa đúng tệp `php.ini` chưa? Sử dụng `phpinfo()` để chắc chắn bạn đã sửa tệp đang được load.
  • Đã khởi động lại web server/PHP-FPM chưa? Thay đổi `php.ini` chỉ có hiệu lực sau khi khởi động lại.
  • Đường dẫn trong `php.ini` có chính xác không? Kiểm tra lại đường dẫn tuyệt đối, dấu ngoặc kép, và dấu phân cách thư mục.
  • Quyền đọc tệp `cacert.pem`: Đảm bảo user chạy web server có quyền đọc tệp này.
  • Extension `php_openssl` và `php_curl` đã được bật chưa? Kiểm tra lại trong `php.ini` và `phpinfo()`.
  • Kiểm tra các vấn đề môi trường khác: Thời gian hệ thống, firewall, proxy, antivirus như đã đề cập ở phần "Kiểm Tra và Gỡ Lỗi Thêm".
  • Phiên bản cURL/OpenSSL: Trong một số trường hợp rất hiếm, phiên bản cURL hoặc OpenSSL cũ có thể gặp vấn đề. Cân nhắc cập nhật môi trường server nếu có thể.
  • Thử Giải pháp 2: Cấu hình đường dẫn `cacert.pem` trực tiếp trong code CodeIgniter để xem liệu có giải quyết được vấn đề không, điều này có thể giúp cô lập lỗi.

Đề xuất Đọc Thêm


Tài liệu tham khảo


Last updated May 5, 2025

AI-assisted research. Sources, methodology and corrections · Source-checked guides and tools · Explore published answers

Ask Ithy AI
Download Article
Delete Article