ERROR 1045: Access denied
El ERROR 1045 es probablemente el primer error que encuentra cualquier persona que empieza con MySQL, y también uno de los que más tiempo hace perder a desarrolladores con experiencia. Aparece cuando el servidor rechaza un intento de conexión porque no puede autenticar al usuario: la contraseña no coincide, la cuenta no existe para el host desde el que te conectas o el cliente no está enviando las credenciales que crees que envía.
Lo importante es entender que este error no significa que el servidor esté caído ni que haya un problema de red. Si ves el 1045, el servidor está funcionando, ha recibido tu conexión y ha decidido rechazarla. Eso ya descarta muchas causas y te permite centrarte en las credenciales y en la cuenta de usuario. En este artículo verás cómo leer el mensaje, cuáles son las causas más habituales, cómo diagnosticar cuál es la tuya y cómo resolverlo, incluido el caso más delicado: haber perdido la contraseña de root.
El mensaje de error
El mensaje completo tiene siempre la misma estructura. Incluye el código numérico, el estado SQL 28000 (que el estándar reserva para errores de autorización), el usuario, el host desde el que MySQL cree que te conectas y una pista sobre si se ha enviado contraseña:
ERROR 1045 (28000): Access denied for user 'app_tienda'@'localhost' (using password: YES)
Cada parte del mensaje aporta información útil. El usuario 'app_tienda' es el nombre que envió el cliente. El host 'localhost' es el origen de la conexión tal como lo ve el servidor, que no siempre coincide con lo que tú esperas: si te conectas por TCP a 127.0.0.1 o desde un contenedor Docker, el host puede ser una dirección IP distinta.
using password: YES frente a using password: NO
La parte final del mensaje es la pista más valiosa y conviene mirarla antes que nada. using password: YES indica que el cliente envió una contraseña y que esa contraseña no ha servido para autenticar ninguna cuenta que coincida con el usuario y el host. En la práctica suele significar contraseña incorrecta o cuenta inexistente para ese host.
ERROR 1045 (28000): Access denied for user 'app_tienda'@'localhost' (using password: NO)
using password: NO significa que el cliente no envió ninguna contraseña. Es típico cuando se olvida la opción -p en la línea de comandos, cuando la variable de entorno o el fichero .env de la aplicación tiene la contraseña vacía o cuando el framework no está leyendo la configuración que crees. Si ves NO y la cuenta tiene contraseña, el problema está en el cliente, no en el servidor.
Causas más habituales
Las causas del 1045 se reducen casi siempre a una discrepancia entre lo que el cliente envía y lo que el servidor tiene registrado en la tabla mysql.user. MySQL identifica cada cuenta por la pareja usuario y host, así que 'app_tienda'@'localhost' y 'app_tienda'@'%' son dos cuentas distintas, con contraseñas y privilegios independientes. Este detalle, que se explica a fondo en el artículo sobre CREATE USER, está detrás de la mayoría de los casos.
La causa más directa es una contraseña incorrecta: un error al escribirla, una contraseña que se cambió en el servidor y no en la aplicación, o una diferencia entre el entorno de desarrollo y el de producción. La segunda causa más frecuente es que la cuenta exista, pero no para el host desde el que te conectas. Si solo creaste 'app_tienda'@'localhost' y la aplicación se conecta desde otro servidor o desde un contenedor, MySQL buscará una cuenta para esa IP, no la encontrará y responderá con 1045.
El caso de root merece mención aparte. En la mayoría de instalaciones solo existe 'root'@'localhost', que no admite conexiones remotas. Si intentas entrar como root desde otra máquina o por una IP, verás el error aunque la contraseña sea correcta. Otra fuente de confusión son los usuarios anónimos (con nombre vacío) que dejan algunas instalaciones antiguas: una cuenta ''@'localhost' es más específica por host que 'app_tienda'@'%', así que MySQL la elige primero y la autenticación falla.
También influyen los detalles del cliente. Una contraseña con caracteres especiales como $, ! o & escrita directamente en la shell puede llegar al servidor modificada, porque la shell interpreta esos caracteres antes de pasárselos a mysql. Y un fichero ~/.my.cnf con credenciales antiguas en la sección [client] puede estar enviando un usuario o una contraseña que no son los que escribes, sin que te des cuenta.
Diagnóstico paso a paso
Antes de cambiar nada, conviene averiguar qué cuentas existen realmente y con qué host y método de autenticación. Para eso necesitas entrar con un usuario que sí funcione, normalmente root o una cuenta de administración. Esta consulta muestra todas las cuentas registradas:
SELECT user, host, plugin, account_locked
FROM mysql.user
ORDER BY user, host;+------------------+-----------+-----------------------+----------------+
| user | host | plugin | account_locked |
+------------------+-----------+-----------------------+----------------+
| app_tienda | localhost | caching_sha2_password | N |
| mysql.infoschema | localhost | caching_sha2_password | Y |
| mysql.session | localhost | caching_sha2_password | Y |
| mysql.sys | localhost | caching_sha2_password | Y |
| root | localhost | caching_sha2_password | N |
+------------------+-----------+-----------------------+----------------+
Con este resultado ya se ve un problema típico: app_tienda solo existe para localhost. Si la aplicación se conecta desde 192.168.1.40, no hay ninguna cuenta que la acepte. La columna plugin indica el método de autenticación, y account_locked confirma que la cuenta no está bloqueada (una cuenta bloqueada da un error diferente, como se explica en bloquear una cuenta).
USER() frente a CURRENT_USER()
Cuando sí consigues conectarte, pero con permisos distintos de los esperados, dos funciones te dicen exactamente con qué cuenta ha emparejado MySQL tu conexión. USER() devuelve el usuario y host que envió el cliente, y CURRENT_USER() devuelve la cuenta de mysql.user que el servidor ha usado para autenticarte:
SELECT USER(), CURRENT_USER();+----------------------+----------------+
| USER() | CURRENT_USER() |
+----------------------+----------------+
| app_tienda@localhost | @localhost |
+----------------------+----------------+
Si ambos valores no coinciden como esperas, has encontrado la causa. En este ejemplo, CURRENT_USER() muestra una cuenta anónima: MySQL ha emparejado la conexión con ''@'localhost' en lugar de con la cuenta de app_tienda. Para revisar qué privilegios tiene la cuenta correcta puedes usar SHOW GRANTS:
SHOW GRANTS FOR 'app_tienda'@'localhost';Descartar el fichero de opciones
Para comprobar si un ~/.my.cnf está interfiriendo, ejecuta el cliente con la opción --no-defaults, que ignora todos los ficheros de opciones. Tiene que ser la primera opción de la línea:
mysql --no-defaults -u app_tienda -pSi así funciona y sin la opción no, revisa la sección [client] de tus ficheros de configuración, que se describen en el artículo sobre el archivo de configuración.
Soluciones
Una vez identificada la causa, la solución suele ser una o dos sentencias. Todas las que siguen requieren conectarte con una cuenta con privilegios de administración.
Cambiar la contraseña de la cuenta
Si la cuenta existe para el host correcto y el problema es que la contraseña no coincide, lo más sencillo es asignarle una nueva con ALTER USER y actualizar la configuración de la aplicación con el mismo valor:
ALTER USER 'app_tienda'@'localhost' IDENTIFIED BY 'T1enda#Prod2026';El cambio es inmediato y no hace falta reiniciar el servidor ni ejecutar FLUSH PRIVILEGES, que solo es necesario cuando se modifican las tablas de permisos directamente con INSERT o UPDATE, algo que no deberías hacer.
Crear la cuenta para el host correcto
Si la cuenta no existe para el host desde el que se conecta la aplicación, créala para ese host concreto y concédele los privilegios que necesita. Es mejor limitar el host a la IP o red de tu servidor de aplicaciones que usar '%', que acepta conexiones desde cualquier origen:
CREATE USER 'app_tienda'@'192.168.1.40' IDENTIFIED BY 'T1enda#Prod2026';
GRANT SELECT, INSERT, UPDATE, DELETE
ON tienda_mysql.*
TO 'app_tienda'@'192.168.1.40';Recuerda que esta cuenta es independiente de 'app_tienda'@'localhost': tiene su propia contraseña y sus propios privilegios. Los detalles de cómo asignar permisos están en el artículo sobre GRANT. Para que la conexión remota funcione, además, el servidor debe escuchar en una interfaz accesible (la variable bind_address). Si no escucha, el error no será 1045, sino uno de conexión como el ERROR 2002.
Si el problema era un usuario anónimo que se adelanta a tu cuenta, lo más limpio es eliminarlo, ya que no aporta nada en un servidor en producción:
DROP USER ''@'localhost';Escribir bien la contraseña en la shell
Cuando la contraseña contiene caracteres especiales, lo más seguro es no escribirla en la línea de comandos y dejar que el cliente la pida con -p sin valor. Si necesitas pasarla en un script, enciérrala entre comillas simples, que evitan que la shell interprete $ o !:
mysql -u app_tienda -p'T1enda#Prod2026' tienda_mysqlFíjate en que no hay espacio entre -p y la contraseña. Si pones un espacio, el cliente interpreta que -p no lleva valor, te pide la contraseña de forma interactiva y toma el texto siguiente como nombre de la base de datos. Para scripts automatizados es mejor guardar las credenciales cifradas con mysql_config_editor.
Recuperar la contraseña de root
Si has perdido la contraseña de root y no tienes ninguna otra cuenta de administración, puedes arrancar MySQL temporalmente sin comprobación de permisos. Es un procedimiento delicado, porque mientras dure cualquiera podría entrar sin contraseña, así que se combina siempre con --skip-networking para impedir conexiones remotas. El primer paso es detener el servicio:
sudo systemctl stop mysqlA continuación arranca el servidor con las dos opciones. En algunas distribuciones el servicio se llama mysqld en lugar de mysql:
sudo mysqld --skip-grant-tables --skip-networking --user=mysql &Ahora conéctate sin contraseña. Como las tablas de permisos no están cargadas, ALTER USER fallaría, así que primero hay que recargarlas con FLUSH PRIVILEGES y después cambiar la contraseña:
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NuevaClaveRoot#2026';Por último, detén este proceso temporal (por ejemplo con mysqladmin -u root -p shutdown, ya con la contraseña nueva) y vuelve a arrancar el servicio de forma normal, como se explica en reiniciar MySQL:
sudo systemctl start mysqlCasos que parecen el 1045 pero no lo son
En Ubuntu y Debian, la cuenta root suele autenticarse con el plugin auth_socket, que no usa contraseña: solo permite entrar al usuario del sistema operativo que se llama igual. Si intentas mysql -u root -p como usuario normal, el error que aparece es muy parecido, pero con otro código:
ERROR 1698 (28000): Access denied for user 'root'@'localhost'
La solución en ese caso es entrar con sudo mysql, sin contraseña, y desde ahí crear una cuenta de administración propia para tus herramientas, en lugar de cambiar el plugin de root.
Algo parecido ocurre con el método de autenticación. Desde MySQL 8.0 el plugin por defecto es caching_sha2_password, y algunos clientes o conectores antiguos no lo soportan. Normalmente eso produce un error distinto, que indica que el plugin no se puede cargar, pero en ciertas combinaciones de conectores antiguos el síntoma es un rechazo de acceso. La solución correcta es actualizar el conector de la aplicación. En MySQL 8.4 el antiguo mysql_native_password viene desactivado por defecto, así que volver a él ya no es una salida recomendable.
Cómo evitar el error
La mayoría de los 1045 se evitan con un poco de orden en la gestión de cuentas. Crea un usuario por aplicación y por origen, con el host lo más concreto posible, y documenta qué cuenta usa cada entorno. Cuando cambies una contraseña, cámbiala en el mismo paso en el servidor y en la configuración de la aplicación, y comprueba la conexión inmediatamente con el cliente de línea de comandos antes de desplegar.
También ayuda revisar de vez en cuando la tabla mysql.user para eliminar cuentas anónimas o que ya no se usan, y agrupar permisos con roles en lugar de repartirlos cuenta por cuenta. Si trabajas con varios servidores, guarda las credenciales con mysql_config_editor en lugar de ficheros .my.cnf en texto plano, para que no se queden credenciales antiguas enviándose sin que lo sepas.
Resumen
El ERROR 1045 indica que el servidor funciona, pero no ha podido autenticar la combinación de usuario, host y contraseña que ha recibido. Empieza siempre por la pista using password: con NO, el problema está en el cliente, que no envía la contraseña; con YES, revisa con SELECT user, host, plugin FROM mysql.user si la cuenta existe para ese host y usa CURRENT_USER() para ver con qué cuenta te ha emparejado MySQL. Con eso, la solución suele ser un ALTER USER para cambiar la contraseña o un CREATE USER para el host correcto. Si el mensaje no dice using password y el código es 1698, estás ante auth_socket en Ubuntu, y si el error habla de no poder conectar, el problema es de red o de servicio, no de credenciales. Para aprender a conectarte correctamente desde el principio, consulta el artículo sobre cómo conectar al servidor.
Escrito por Eduardo Lázaro
