A reference is formally a type; or at least you can read things like "if T is a reference type" in the C++ standard itself.
However, your question is perfectly legitimate in that references have very confusing semantics. They are not quite first-class types (for example, you can't have a reference-to-reference or a pointer-to-reference), and in my opinion that's because C++ managed to conflate two different kinds of types with how it defines and uses references.
What a reference really does is it gives an alternate name to an already-existing object (value). What does this mean? It means that it doesn't "qualify" the type of the value it refers to per se; it rather qualifies the name ("variable", "storage") itself that is used for referencing the value.
In C++, the semantics of a type and a value often depends on additional properties of the storage where the object/value is stored. This is not always explicit, and that's what confuses people.
I think because C++ heavily relies on exposing the concept of "storage" (rather than hiding it as an implementation detail), there really should be two type systems: one for pure values themselves, and one for storage, where the type system for storage should be a superset of the type system for values.
Another example where a very similar issue appears is CV-qualification. It's not an object/value itself that should be const or volatile. It's the storage containing that value that may or may not be mutable, and may or may not need to be protected from certain load/store optimizations. Again, this could be better expressed if there was a way to express these properties of types separately in the case of values and storage.